Adaの日本語情報を増やす(3)

RecordはとばしてTask。

もっとも基本的なTaskの使い方はprocedureなどの宣言部で定義を行う方法だ。

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;

procedure Sample is

   procedure Start_Task is
      task Task1;
      task body Task1 is
      begin
         Put_Line("Task1 BEGIN");
      end Task1;

      task Task2;
      task body Task2 is
      begin
         Put_Line("Task2 BEGIN");
      end Task2;

   begin -- ここでTaskの処理がはじまる
      Put_Line("Start_Task BEGIN"); -- すべてのTaskが開始されるまで待たされる
   end Start_Task;

begin
   Start_Task;
   Put_Line("Tasks are DONE"); -- Start_Taskが完了するまで待たされる。
end Sample;

このコードの実行結果は、なにかイレギュラーな要素がない限り次のようになる。

Task1 BEGIN
Task2 BEGIN
Start_Task BEGIN -- Task1, Task2より後にきている
Tasks are DONE

宣言部で単純にTaskを定義した場合、そのTaskの開始は親単位(つまり、そのタスクを宣言部に持つ関数とか)がbeginした瞬間(「直前」といった方がわかりやすいかもしれない)となる。上記のコードのようにタスクが複数(Task1, Task2)有る場合は次のような感じで実行されてていく。

親単位の宣言部が確立された段階で各タスクのメモリは確保されるが、実行はされない。親単位がbeginされると同時に、各タスクの宣言部が順番に確立され、beginされていく。並列(平行)処理が行われるのはここからで、Task1の確立が完了しbeginされると、Task1の処理と同時にTask2の確立が開始される。

なお、このとき注意すべきなのは、すべてのTaskが開始されるまで、親単位の並列実行は待たされるということだ。上の実行結果を見れば分かるとおり、一見1番最初に出力されそうな「Start_Task BEGIN」がTask1とTask2の後に来ていのが分かるだろう。

また、親単位が制御を「親の親」に戻すのは、自分の持つすべてのTaskが完了してからである。逆に言えば、「親の親」に制御が戻った時点で孫Taskがすべて完了していることが保証されるので、Taskがすべて完了したことをいちいち確認する必要がない。いわゆる「待ち合わせ」を簡単に行うできる、といえるだろう。上のコードでは「Tasks are DONE」が表示された時点で、Task1、Task2が完了していることが保証されている。

閑話休題。

それにしても、

select
   accept Receive do
      Do_Something;
   end Receive;
else
   null;
end select;

ってもっと簡単に書けないのかなぁ。「キューが空の時は待たないでスルー」って結構使うと思うんだけどな。


if Receive'Count > 0 then
   accept Receive do
      Do_Something;
   end Receive;
end if;

これでもいけるけど、やっぱり複雑だ。