home *** CD-ROM | disk | FTP | other *** search
/ Solo Programadores 22 / SOLO_22.iso / docs / lovelace / lesson13.les < prev    next >
Encoding:
Text File  |  1995-11-21  |  17.2 KB  |  449 lines

  1. <COMMENT This is a lesson file for the Lovelace Ada tutorial>
  2. <COMMENT A program called genlesson is used to transform this file into a set>
  3. <COMMENT of useful HTML files for use by Mosaic & other WWW browsers.>
  4.  
  5. <COMMENT  Edit the following lines. >
  6. <TUTOR NAME="Lovelace">
  7. <LESSON NUMBER=13>
  8. <AUTHOR NAME="David A. Wheeler" EMAIL="wheeler@ida.org">
  9. <AUTHOR ADDRESS="<A HREF="dwheeler.htm">David A. Wheeler (wheeler@ida.org)</A>">
  10. <COMMENT $Id: lesson13.les,v 1.4 1995/09/22 21:38:21 wheeler Exp wheeler $ >
  11.  
  12. <COMMENT  You'll probably want to uncomment and edit these lines: >
  13. <COMMENT  <PREVIOUS_LESSON LOCATION="URL_of_directory/" >
  14. <COMMENT  <NEXT_LESSON LOCATION="URL_of_directory/" >
  15.  
  16. <COMMENT A lesson is divided into 1 or more "sections".>
  17. <COMMENT Each section has a title; SECTION starts a new section.>
  18.  
  19. <SECTION NAME="Tasking Basics">
  20. The Ada language includes built-in support for concurrent (parallel)
  21. processing with Ada <EM>tasks</EM>.
  22. Ada tasks run concurrently and can interact with each other using a few
  23. different mechanisms.
  24. In essence, each Ada task works as though it were running on
  25. a separate computer.
  26. Tasks are called by some people ``threads'' or ``light-weight processes''.
  27. More general terms for task-like behavior include ``process'',
  28. ``agent'', and ``active object''.
  29. <P>
  30. Why would you want to use tasks?
  31. Well, here's one example - let's imagine you're developing a
  32. World Wide Web browser.
  33. Such a browser must download information through some (slow) communication
  34. device and then display the information.
  35. Now, you could wait until all the information was available and then
  36. display it, but that would make the user wait for a possibly long time.
  37. A better solution would be to have two tasks - one that downloads the
  38. information, and another that displays the information.
  39. As the first task gathers more information, it could pass portions of
  40. what it's downloaded along to the second task.
  41. The second task could work to display information to the user, even if
  42. the first task hasn't finished gathering its data yet.
  43. Both tasks could then work ``simultaneously''.
  44. <P>
  45. Tasks can be started up (activated) and stopped (terminated).
  46. There are a variety of ways tasks can communicate with each other
  47. once they are activated; the main ways are:
  48. <UL>
  49. <LI>
  50. Tasks can wait for other tasks to complete.
  51. <LI>
  52. Tasks can send messages between each other; this is called a
  53. rendezvous.
  54. <LI>
  55. Tasks can use `protected objects', which provide exclusive read-write access
  56. to data.
  57. Protected objects are new to Ada 95.
  58. </UL>
  59. <P>
  60. We'll discuss these different communication methods in the next few sections.
  61. <P>
  62. Some computing systems actually have several computers built into them.
  63. If the Ada compiler and/or operating system can do so, different tasks
  64. may be run on different machines, which may significantly speed up
  65. execution of a program.
  66. <P>
  67. There are some important caveats about tasks:
  68. <UL>
  69. <LI>
  70. Ada can't create what doesn't exist.
  71. On single-CPU machines, Ada must simulate 
  72. multiple computers on a single computer, and there
  73. is some overhead to doing that (called the ``context switch time'').
  74. <LI>
  75. Tasks can be under-used or over-used.
  76. Some people create hundreds of unnecessary tasks,
  77. producing slow, terrible programs.
  78. Like any other tool, use tasks appropriately.
  79. As a rule of thumb, check what you're doing if you're using more
  80. than ten to twenty tasks on a single CPU, especially if more than a
  81. few of them are simultaneously active.
  82. Also, while there may be many low-level (hardware-level) tasks that
  83. do trivial work, tasks should generally not simply
  84. do a trivial operation and then send information on to yet another task.
  85. Do not follow these rules slavishly; think of these as naive
  86. guidelines until you understand tasking more completely.
  87. <LI>
  88. If there's an underlying operating system, the operating system
  89. must provide some reasonable support for Ada tasks to work well.
  90. Modern operating systems that support threads or light-weight processes generally
  91. work nicely; such
  92. operating systems include Windows NT, Windows 95, OS/2, Mach, and Solaris.
  93. Nearly all real-time operating systems provide mechanisms sufficient for
  94. implementing Ada tasks.
  95. Windows 3.1 and some old Unix systems do not provide such support, and
  96. this causes some minor but annoying limitations on those
  97. systems as we'll discuss later.
  98. MS-DOS doesn't directly support threads, but it's such a primitive operating
  99. system that tasks can be created by simply taking over the entire machine.
  100. </UL>
  101. <P>
  102. Technically speaking, an Ada program always includes at least one
  103. task, called the <EM>environment</EM> task; the main (starting) subprogram
  104. runs in this environment task.
  105.  
  106. <QUESTION Type=Multiple-Choice>
  107. Software XYZ is running on a single processor and has
  108. 10,000 tasks.
  109. There's a task for every switch on an input panel
  110. and a task for every light on a display panel.
  111. Does this sound like a good design?
  112. <CHOICES>
  113. <CHOICE ANS=1>This is probably a good design.
  114. <CHOICE ANS=2>This is probably not a good design.
  115. </CHOICES>
  116. <ANSWER ANS=2>
  117. <RESPONSES>
  118. <WHEN ANS=1>
  119. No, this is probably not a good design.
  120. Remember, the rule of thumb I gave earlier was to consider carefully
  121. what you're doing if there are more than a dozen tasks, or if each
  122. task is doing a trivial operation.
  123. <P>
  124. Now, this <EM>might</EM> be the best approach
  125. depending on information we don't have,
  126. but warning bells should go off in your mind, suggesting that you may
  127. have a significant performance problem with this system.
  128. <WHEN ANS=2>
  129. Right.
  130. Having this many tasks <EM>might</EM> be the best approach
  131. depending on other information that wasn't presented.
  132. However, warning bells should go off in your mind when a system has
  133. this many tasks - you're likely to have a performance problem
  134. with this system due to overuse of tasks.
  135. </RESPONSES>
  136.  
  137. <SECTION NAME="Creating and Communicating with Tasks">
  138. Let's start by looking at an example of a trivial task.
  139. Let's create a type of task that waits for a "Start" request,
  140. prints a line of text for a number of times, and then terminates itself.
  141. <P>
  142. We'll wait a second between printing each line of text, which will
  143. help you see what it does.
  144. To make it more interesting, we'll include in the Start request
  145. the message to be printed and the number of times it's to print.
  146. <P>
  147. First, let's create a task type; the task type will be called Babbler, and
  148. its declaration could look like the following:
  149. <P>
  150. <PRE>
  151.   with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
  152.  
  153.   task type Babbler is
  154.     entry Start(Message : String; Count : Natural);
  155.   end Babbler;
  156. </PRE>
  157. <P>
  158. Just like packages and subprograms, tasks have a declaration and a body.
  159. The task body could look like this:
  160. <P>
  161. <PRE>
  162.   with Ustrings;   use Ustrings;
  163.  
  164.   task body Babbler is
  165.     Babble : Unbounded_String;
  166.     Maximum_Count : Natural;
  167.   begin
  168.     accept Start(Babble, Maximum_Count); -- Wait for Start message.
  169.     for I in 1 .. Maximum_Count loop
  170.       Put_Line(Babble);
  171.       delay 1.0;       -- Wait for one second.
  172.     end loop;
  173.     -- We're done, exit task.
  174.   end Babbler;
  175. </PRE>
  176. <P>
  177. Here's a short procedure to demonstrate this task type; we'll call it
  178. the procedure Noise. Noise will create two tasks of the given task type
  179. and send them Start messages. Note how similar creating a task is to
  180. creating a variable from an ordinary type:
  181. <P>
  182. <PRE>
  183.   with Babbler, Ada.Strings.Unbounded;
  184.   use  Babbler, Ada.Strings.Unbounded;
  185.  
  186.   procedure Noise is
  187.     Babble_1 : Babbler;  -- Create a task.
  188.     Babble_2 : Babbler;  -- Create another task.
  189.   begin
  190.     -- At this point we have two active tasks, but both of them
  191.     -- are waiting for a "Start" message. So, send them a Start.
  192.     Babble_1.Start(To_Unbounded_String("Hi, I'm Babble_1"), 10);
  193.     Babble_2.Start(To_Unbounded_String("And I'm Babble_2"), 6);
  194.   end Noise;
  195. </PRE>
  196. <P>
  197. A procedure that declares a task instance, like procedure Noise,
  198. is called a <EM>Master</EM>.
  199. A master must wait for all its tasks to terminate before it can terminate,
  200. so Noise will wait until Babble_1 and Babble_2 have exited before
  201. it exits.
  202. <P>
  203. When procedure Noise makes a ``call'' to Babble_1 and Babble_2's `Start'
  204. entry, it is performing what is called a <EM>rendezvous</EM>.
  205.  
  206. <QUESTION Type=Multiple-Choice>
  207. Should you see text from Babble_1 and Babble_2 interleaved on your
  208. display when you run Noise?
  209. <CHOICES>
  210. <CHOICE ANS=1>Yes.
  211. <CHOICE ANS=2>No.
  212. </CHOICES>
  213. <ANSWER ANS=1>
  214. <RESPONSES>
  215. <WHEN ANS=2>
  216. Sorry, you should see Babble_1 and Babble_2 interleaving their text
  217. (at least on a line-by-line basis).
  218. Babble_1 and Babble_2 will run concurrently, as though they
  219. were running on different machines (depending on your hardware, they
  220. may even run on different machines).
  221. </RESPONSES>
  222.  
  223.  
  224. <SECTION NAME="Protected Types">
  225. A protected type is a passive data object that provides protection
  226. of data consistency even when multiple tasks attempt to
  227. access its data.
  228. Protected types are very efficient, which is why they were added
  229. to Ada in 1995.
  230. Protected types can be considered to be a very advanced form of
  231. "semaphore" or "monitor".
  232. <P>
  233. A protected type contains data that tasks can access only through
  234. a set of protected operations defined by the developer. 
  235. There are three kinds of protected operations:
  236. <OL>
  237. <LI>
  238. Protected functions, which provide read-only access to the internal data.
  239. Multiple tasks may simultaneously call a protected function.
  240. <LI>
  241. Protected procedures, which provide exclusive read-write access to the
  242. internal data.
  243. When one task is running a protected procedure, no other task
  244. can interact with the protected type.
  245. <LI>
  246. Protected entries, which are just like protected procedures except
  247. that they add a "barrier".
  248. A barrier is some Boolean expression that must become true before
  249. the caller may proceed.
  250. The barrier would usually depend on some internal data value protected
  251. by the protected type.
  252. If the barrier is not true when the caller makes a request, the
  253. caller is placed in a queue to wait until the barrier becomes true.
  254. </OL>
  255. <P>
  256. Protected types tend to be very efficient, since
  257. high-overhead operations called "full context switches"
  258. aren't usually necessary to implement them.
  259. Protected types are often implemented using techniques such as
  260. interrupt disables,
  261. <A HREF="http://lglwww.epfl.ch/Ada/LRM/9X/rm9x/rm9x-D-03.html">priority
  262. ceiling locking</A>, or spin locks.
  263. In fact, protected types are often more efficient than using
  264. semaphores directly, which is a little surprising;
  265. see the
  266. <A HREF="http://lglwww.epfl.ch/Ada/LRM/9X/Rationale/rat95html/rat95-p2-9.html#1">Ada Rationale (Part 2, section 9.1.3)</A>
  267. if you're curious why protected types can be so efficient.
  268. However, this also means that any protected operation should be
  269. short and fast; significant processing should be done elsewhere.
  270. Protected operations generally should do things like increment
  271. or decrement a value, set a flag, set an access value or two,
  272. or other similar quick operations.
  273. Lengthy operations may increase the maximum system latency (the time it
  274. takes for the system to respond to a new situation), which in many
  275. systems is a bad thing.
  276. <P>
  277. A protected type can be created as a single instance (i.e. a single
  278. protected variable) or as a full Ada type.
  279. As the latter you can do anything you would do with a regular type,
  280. including placing them in records or arrays.
  281.  
  282. <QUESTION Type=Multiple-Choice>
  283. Let's say you're creating a protected type and you want to create an
  284. operation that changes the protected type's data.
  285. This operation can always occur - it doesn't need to wait for some
  286. special circumstance.
  287. Which of the following should this operation be?
  288.  
  289. <CHOICES>
  290. <CHOICE ANS=1>Protected function.
  291. <CHOICE ANS=2>Protected procedure.
  292. <CHOICE ANS=3>Protected entry.
  293. </CHOICES>
  294. <ANSWER ANS=2>
  295.  
  296. <SECTION NAME="Protected Types Part 2">
  297. Now that you know the different types of protected operations,
  298. declaring a protected type will make more sense.
  299.  
  300. <P>
  301. Here's an example - this is a semaphore implemented using
  302. protected types.
  303. You can request to "Seize" the semaphore;
  304. once it is Seized no other task can Seize it until it is Released.
  305. <P>
  306. <PRE>
  307.   protected type Resource is
  308.     entry Seize;        -- Acquire this resource exclusively.
  309.     procedure Release;  -- Release the resource.
  310.   private
  311.     Busy : Boolean := False;
  312.   end Resource;
  313.  
  314.   protected body Resource is
  315.     entry Seize when not Busy is
  316.     begin
  317.       Busy := True;
  318.     end Seize;
  319.  
  320.     procedure Release is
  321.     begin
  322.       Busy := False;
  323.     end Release;
  324.   end Resource;
  325. </PRE>
  326. <P>
  327. Here's an example of creating a protected variable that
  328. is an instance of the protected type Resource:
  329. <P>
  330. <PRE>
  331.   Control : Resource;
  332. </PRE>
  333. <P>
  334. And here's an example of using it:
  335. <P>
  336. <PRE>
  337.   Control.Seize;
  338.   Diddle;
  339.   Control.Release;
  340. </PRE>
  341. <P>
  342. Here's the
  343. <A HREF="bnf.htm">BNF</A>
  344. for a protected (type) declaration and its corresponding
  345. protected body:
  346. <P>
  347. <PRE>
  348.   protected_declaration ::= "protected" [ "type" ] identifier "is"
  349.                             { protected_operation_declaration }
  350.                             [ "private" { protected_element_declaration } ]
  351.                             "end" [ identifier ]
  352.   protected_operation_declaration ::= subprogram_declaration |
  353.                                       entry_declaration
  354.   protected_element_declaration ::= protected_operation_declaration |
  355.                                     component_declaration
  356.   
  357.   protected_body ::= "protected" "body" identifier "is"
  358.                      { protected_operation_item }
  359.                      "end" [ identifier ] ";"
  360. </PRE>
  361. <P>
  362. I've shown how to implement a semaphore using protected types
  363. because semaphores are a well-known construct for concurrent programs.
  364. However, it's better to use protected types directly instead of
  365. trying to build task interaction constructs
  366. using semaphores as the building block.
  367. One reason is that semaphores are notoriously hard to use correctly
  368. for complex task interactions - once multiple semaphores are involved 
  369. it can be difficult to get the interactions correct for all cases
  370. (truly getting such interactions right may require developing a
  371. formal proof of a concurrent protocol, a really difficult thing to do).
  372. Also, when exceptions occur Ada can handle protected types automatically,
  373. which is easy to get wrong when doing it "by hand".
  374. Besides, the protected type may be more efficient.
  375. <P>
  376. One particularly useful use of protected types is to implement
  377. a buffered queue of messages between tasks.
  378. See the
  379. <A HREF="http://lglwww.epfl.ch/Ada/LRM/9X/Rationale/rat95html/rat95-p2-9.html#1">Ada Rationale (Part 2, section 9.1.2)</A>
  380. for an example of this (the Mailbox_Pkg protected type).
  381.  
  382. <SECTION NAME="Other Tasking Issues">
  383.  
  384. The underlying operating system does affect tasking,
  385. particularly if the operating system does not provide
  386. certain minimal capabilities (i.e. thread support).
  387. Here are two effects that you need to be aware of:
  388. <OL>
  389. <LI>
  390. Some operating systems (such as Microsoft Windows 3.1 and many older
  391. Unixes) do not support threads (lightweight processes), but instead
  392. only support regular processes (sometimes called heavyweight processes).
  393. The difference is that threads can share memory, while processes
  394. generally do not.
  395. On systems which do not support threads,
  396. if any task makes an operating system request
  397. (say, to get input), <EM>all</EM> the Ada tasks are usually halted until the
  398. operating system completes the request.
  399. This is because most Ada compilers in such environments put all of the
  400. Ada tasks into a single operating system process and then simulate
  401. the tasking inside the process.
  402. The operating system can't distinguish
  403. between the different Ada tasks in the single process,
  404. so all Ada tasks get stopped.
  405. As more operating systems become more capable this is becoming
  406. less of a problem, and this is generally not a problem for
  407. embedded systems (where Ada has complete control over the system
  408. or is running on a real-time operating system).
  409. <LI>
  410. Some operating systems make it difficult or inefficient to share time
  411. between tasks.
  412. This is called ``time slicing''.
  413. Because of this, an Ada compiler is permitted to keep running one
  414. task until that task tries to communicate with another task or waits
  415. (using the delay statement).
  416. This kind of behavior is called "cooperative multitasking", because
  417. tasks of equal priority must cooperate to share the CPU.
  418. Most people prefer Ada implementations that have a
  419. "preemptive time-slicing multitasking" system, where tasks of equal priority
  420. are forced to share the CPU.
  421. If you have to deal with an Ada compiler that only supports cooperative
  422. multitasking, consider inserting "delay 0.0" statements in each task
  423. in various places to give other tasks a chance to run.
  424. Check your compiler documentation; some compilers permit you to choose
  425. time-slicing or cooperative behavior.
  426. Again, most of today's Ada compilers provide the (more general)
  427. time-slicing multitasking behavior.
  428. </OL>
  429. <P>
  430. There's much more about tasking that this version of Lovelace doesn't cover.
  431. For example:
  432. <UL>
  433. <LI>
  434. You can create tasks that exist for as long as the program runs, by
  435. declaring the task in a package declaration or body (the same
  436. way you can declare a global variable).
  437. <LI>
  438. The <EM>accept</EM> statement can have an optional <EM>do...</EM> part;
  439. until this part completes, the caller of a task cannot continue, making
  440. it possible to hold the caller until something has completed.
  441. <LI>
  442. Entries are queued in first-in-first-out order, and can be re-queued
  443. if desired.
  444. </UL>
  445. <P>
  446. If you're already familiar with tasking, please feel free to volunteer
  447. to extend this lesson!
  448.  
  449.