2

我需要一些帮助,也需要一些见解。这是 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) 使用编译指示,结果:在 100 次尝试中,出现 2:84,出现 1:14,出现 0:2。

这是出乎意料的,因为两个结果几乎相同。这意味着输出具有相同的行为。

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

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

4

3 回答 3

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 的任务将阻塞,直到其他两个报告。

于 2010-03-22T21:00:31.273 回答
1

好吧,这些 pragma 只是优先考虑系统上的任务,它们不保证对这些变量有任何互斥。

可能有一些系统就足够了。然而,现在大多数 Ada 实现都将 Ada 任务映射到操作系统线程,并且现在大多数消费类 PC 都有多个处理器,并且可以在它们之间溢出它们的线程。当最高优先级线程正在运行时,没有什么可以阻止操作系统在您的第二个处理器上调度下一个优先级较低的线程。

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

如果你想对这些变量进行互斥,你需要实现它。要么将变量控制权交给一项任务,然后使用集合点从其他任务中修改它们,要么考虑将它们放入受保护的对象中。我建议后者,因为约会可能更难以正确。但是,如果您想以特定方式对调用进行排序,则可能是主控制器任务调用其他任务的集合点。

于 2010-03-23T13:51:57.253 回答
0

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

WITH Ada.Text_IO; 使用 Ada.Text_IO;

WITH Ada.Integer_Text_IO; 使用 Ada.Integer_Text_IO;

带系统;使用系统;

程序 xyzsimple 是

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

任务类型 xyz;

T:xyz;

任务主体 xyz 是

开始

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

结束 xyz;

开始放(“z =”);

放(z);

结束xyz​​simple;

这个程序总是输出 z = 2,但它并不能说明我想要做什么。这个程序是确定性的,没有进入并发范式!此外,该节目将永远不会出现 TED 提到的“RACE CONDITION”。

于 2010-03-25T03:15:29.230 回答