Ada 95 Quality and Style Guide Chapter 6

Chapter 6: Concurrency - TOC - 6.1 CONCURRENCY OPTIONS

6.1.2 Tasks


  • Use tasks to model selected asynchronous threads of control within the problem domain.
  • Consider using tasks to define concurrent algorithms.
  • Consider using rendezvous when your application requires synchronous unbuffered communication.

  • example

    The naturally concurrent objects within the problem domain can be modeled as Ada tasks.

    -- The following example of a stock exchange simulation shows how naturally
    -- concurrent objects within the problem domain can be modeled as Ada tasks.
    -- Protected objects are used for the Display and for the Transaction_Queue
    -- because they only need a mutual exclusion mechanism.
    protected Display is
       entry Shift_Tape_Left;
       entry Put_Character_On_Tape (C : in Character);
    end Display;
    protected Transaction_Queue is
       entry Put (T : in     Transaction);
       entry Get (T :    out Transaction);
       function Is_Empty return Boolean;
    end Transaction_Queue;
    -- A task is needed for the Ticker_Tape because it has independent cyclic
    -- activity.  The Specialist and the Investor are best modeled with tasks
    -- since they perform different actions simultaneously, and should be
    -- asynchronous threads of control.
    task Ticker_Tape;
    task Specialist is
       entry Buy  (Order : in Order_Type);
       entry Sell (Order : in Order_Type);
    end Specialist;
    task Investor;
    task body Ticker_Tape is
          if not More_To_Send (Current_Tape_String) and then
             not Transaction_Queue.Is_Empty
             Transaction_Queue.Get (Current_Tape_Transaction);
             ... -- convert Transaction to string
          end if;
          if More_To_Send (Current_Tape_String) then
             Display.Put_Character_On_Tape (Next_Char);
          end if;
          delay until Time_To_Shift_Tape;
          Time_To_Shift_Tape := Time_To_Shift_Tape + Shift_Interval;
       end loop;
    end Ticker_Tape;
    task body Specialist is 
             accept Buy  (Order : in Order_Type) do
             end Buy;
             accept Sell (Order : in Order_Type) do
             end Sell;
             -- match orders
             Transaction_Queue.Put (New_Transaction);
          end select;
       end loop;
    end Specialist;
    task body Investor is
          -- some algorithm that determines whether the investor
          -- buys or sells, quantity, price, etc
          if ... then
             Specialist.Buy (Order);
          end if;
          if ... then
             Specialist.Sell (Order);
          end if;
       end loop;
    end Investor;

    Multiple tasks that implement the decomposition of a large, matrix multiplication algorithm are an example of an opportunity for real concurrency in a multiprocessor target environment. In a single processor target environment, this approach may not be justified due to the overhead incurred from context switching and the sharing of system resources.

    A task that updates a radar display every 30 milliseconds is an example of a cyclic activity supported by a task.

    A task that detects an over-temperature condition in a nuclear reactor and performs an emergency shutdown of the systems is an example of a task to support a high-priority activity.


    These guidelines reflect the intended uses of tasks. They all revolve around the fact that a task has its own thread of control separate from the main subprogram (or environment task) of a partition. The conceptual model for a task is a separate program with its own virtual processor. This provides the opportunity to model entities from the problem domain in terms more closely resembling those entities and the opportunity to handle physical devices as a separate concern from the main algorithm of the application. Tasks also allow naturally concurrent activities that can be mapped to multiple processors within a partition when available.

    You should use tasks for separate threads of control. When you synchronize tasks, you should use the rendezvous mechanism only when you are trying to synchronize actual processes (e.g., specify a time-sensitive ordering relationship or tightly coupled interprocess communication). For most synchronization needs, however, you should use protected objects (see Guideline 6.1.1), which are more flexible and can minimize unnecessary bottlenecks. Additionally, passive tasks are probably better modeled through protected objects than active tasks.

    Resources shared between multiple tasks, such as devices, require control and synchronization because their operations are not atomic. Drawing a circle on a display might require that many low-level operations be performed without interruption by another task. A display manager would ensure that no other task accesses the display until all these operations are complete.

    < Previous Page Search Contents Index Next Page >
    1 2 3 4 5 6 7 8 9 10 11
    Appendix References Bibliography