Ada并发中的问题

时间:2010-03-22 16:14:32

标签: concurrency ada pragma

我需要一些帮助,也需要一些见解。这是Ada-2005中的一个程序,它有3个任务。输出为'z'。如果3个任务没有按照它们在程序中的位置顺序发生,那么输出可以从z = 2,z = 1到z = 0变化(这很容易在程序中看到,互斥是为了确保输出是z = 2)。

WITH Ada.Text_IO; USE Ada.Text_IO;
WITH Ada.Integer_Text_IO; USE Ada.Integer_Text_IO; 
WITH System; USE System;

procedure xyz is 
   x : Integer := 0; 
   y : Integer := 0; 
   z : Integer := 0;

   task task1 is
      pragma Priority(System.Default_Priority + 3);
   end task1;

   task task2 is
      pragma Priority(System.Default_Priority + 2);
   end task2;

   task task3 is
      pragma Priority(System.Default_Priority + 1);
   end task3;

   task body task1 is
   begin
      x := x + 1;
   end task1;

   task body task2 is
   begin
      y := x + y;
   end task2;

   task body task3 is
   begin
      z := x + y + z;
   end task3;

begin 
   Put(" z = ");
   Put(z); 
end xyz;

我第一次尝试这个程序

(a)没有编译指示,结果:在100次尝试中,出现2:86,出现1:10,出现0:4。

然后

(b)用pragma,结果:在100次尝试中,出现2:84,出现1:14,出现0:2。

这是意料之外的,因为2个结果几乎相同。这意味着编译指示或没有编译指示输出具有相同的行为。

那些Ada并发大师们请谈谈这个话题。还邀请了具有信号量的替代解决方案(如果可能的话)。

此外,在我看来,对于一个关键过程(就像我们对Ada所做的那样),对于pragma,结果应该始终为z = 2,100%,因此或者该程序应该被称为85%的关键! ! (Ada不应该这样)

3 个答案:

答案 0 :(得分:5)

执行这三个操作的受保护对象可能看起来像这样。但请注意,所有这一切都是为了确保三个变量x,y和z与更新发生的顺序一致;它没有说明订单。

   protected P is
      procedure Update_X;
      procedure Update_Y;
      procedure Update_Z;
      function Get_Z return Integer;
   private
      X : Integer := 0;
      Y : Integer := 0;
      Z : Integer := 0;
   end P;
   protected body P is
      procedure Update_X is
      begin
         X := X + 1;
      end Update_X;
      procedure Update_Y is
      begin
         Y := Y + X;
      end Update_Y;
      procedure Update_Z is
      begin
         Z := X + Y + Z;
      end Update_Z;
      function Get_Z return Integer is
      begin
         return Z;
      end Get_Z;
   end P;

另一方面,为了确保三个任务以正确的顺序“提交结果”,您可以重写P,以便调用说Update_Y将阻塞,直到调用Update_X:Get_Z现在必须是具有out参数而不是函数的条目。

  protected P is
      entry Update_X;
      entry Update_Y;
      entry Update_Z;
      entry Get_Z (Result : out Integer);
   private
      X_Updated : Boolean := False;
      Y_Updated : Boolean := False;
      Z_Updated : Boolean := False;
      X : Integer := 0;
      Y : Integer := 0;
      Z : Integer := 0;
   end P;
   protected body P is
      entry Update_X when True is
      begin
         X := X + 1;
         X_Updated := True;
      end Update_X;
      entry Update_Y when X_Updated is
      begin
         Y := Y + X;
         Y_Updated := True;
      end Update_Y;
      entry Update_Z when Y_Updated is
      begin
         Z := X + Y + Z;
         Z_Updated := True;
      end Update_Z;
      entry Get_Z (Result : out Integer) when Z_Updated is
      begin
         Result := Z;
      end Get_Z;
   end P;

这三项任务现在可以优先考虑。但调用Update_Z的任务将阻塞,直到其他两个报告为止。

答案 1 :(得分:1)

那么,那些pragma只是优先考虑系统上的任务,它们不保证对这些变量进行任何形式的互斥。

可能是一些足够的系统。但是,如今大多数Ada实现都将Ada任务映射到操作系统线程,而目前大多数消费者PC都有多个处理器,并且可以在其中散布自己的线程。在优先级最高的线程运行时,没有什么能阻止操作系统在第二个处理器上调度下一个优先级较低的线程。

程序中的这种行为称为“竞争条件”。

如果你想要对这些变量进行互访,你需要实现它。将变量控制权交给一个任务,并使用集合点从其他任务中修改它们,或者考虑将它们放入protected objects。我建议使用后者,因为会合可能要难得多。但是,如果要以特定方式对呼叫进行排序,则可能需要在其他任务上调用集合点的主控制器任务。

答案 2 :(得分:0)

这是一个程序,它是上述的顺序变体! ....只有一项任务(如果我使用单一程序,可以避免,如Marc C和T.E.D所建议的那样)

  

WITH Ada.Text_IO;使用Ada.Text_IO;

     

WITH Ada.Integer_Text_IO;使用   Ada.Integer_Text_IO;

     

WITH系统;使用系统;

     

程序xyzsimple是

     

x:整数:= 0;   y:整数:= 0;   z:整数:= 0;

     

任务类型xyz;

     

T:xyz;

     

任务正文xyz是

     

开始

     

x:= x + 1;
  y:= x + y;
  z:= x + y + z;

     

结束xyz​​;

     

开始Put(“z =”);

     

把(Z);

     

结束xyz​​simple;

这个程序总是输出z = 2,但它不能说明我想要做的事情。这个程序是确定性的,而不是并发的范式!此外,该计划将永远不会出现T.E.D提到的“种族条件”。