在Ada中的信号量

8
这是一个任务,要求我按照以下描述,在Ada中实现信号量Semaphore。
我已经在producerconsumer_sem.adb中调用了Semaphore.adb来使用此Semaphore。 我获得了以下输出。
  1. 我不确定信号量的初始化是否正确:S: CountingSemaphore(1,1);

  2. 我不知道何时调用S.waitS.Signal。 现在我随机在生产者将项目放入缓冲区X := I;之前调用了S.wait,在X := I;后调用了S.Signal。 这是正确的方法吗?

生产者-消费者问题 程序producerconsumer.adb实现了生产者-消费者问题的不可靠实现,其中数据很可能会丢失。 在接下来的内容中,您将使用三种不同的通信机制来实现生产者-消费者问题的可靠实现。

信号量

Ada语言没有直接提供用于信号量的库函数。但是,可以通过受保护对象实现信号量。 在文件Semaphores.ads中创建包规范Semaphore,并在文件Semaphores.adb中创建相应的包体,以实现计数信号量。 课程页面上提供了该软件包的骨架。

使用信号量包进行生产者-消费者问题的可靠实现。 修改文件producerconsumer.adb,并将最终代码保存为producerconsumer_sem.adb。 要使用信号量程序包,它应安装在与producerconsumer_sem.adb相同的目录中。 然后可以通过以下方式访问它:

with Semaphores; use Semaphores;

输出:

输出结果: 1 1 1 2 2 3 4 4 5 6 6 7 7 8 9 9 9 10 11 11 11 12 12 13 13 13 14 15 15 16 16 17 18 18 18 19 20 20 21 21 22 22 23 24 24 24 24 25 25 26 27 27 28 29 29 30 30 31 31 32 32 33 33 33 34 35 35 35 36 36 37 37 37 38 38 38 39 40 40 40

该套件

package Semaphores is
   protected type CountingSemaphore(Max: Natural; Initial: Natural)  is
      entry Wait;
      entry Signal;
   private
      Count : Natural := Initial;
      MaxCount : Natural := Max;
   end CountingSemaphore;
end Semaphores;

我实现了信号量 semaphores.adb
package body Semaphores is
   protected body CountingSemaphore is
   entry Wait when Count > 0 is
    begin
    Count := Count - 1;

    end Wait;
      entry Signal when Count < MaxCount is
    begin
    Count := Count + 1;

    end Signal;
   end CountingSemaphore;
end Semaphores;

producerconsumer_sem.adb 模块

with Ada.Text_IO;
use Ada.Text_IO;

with Ada.Real_Time;
use Ada.Real_Time;

with Ada.Numerics.Discrete_Random;

with Semaphores;
use Semaphores;

procedure ProducerConsumer_sem is

   X : Integer; -- Shared Variable
   N : constant Integer := 40; -- Number of produced and comsumed variables

   S: CountingSemaphore(1,1);
   --S1: CountingSemaphore(1,1);

   pragma Volatile(X); -- For a volatile object all reads and updates of
                       -- the object as a whole are performed directly
                       -- to memory (Ada Reference Manual, C.6)

   --Random Delays
   subtype Delay_Interval is Integer range 50..250;
   package Random_Delay is new Ada.Numerics.Discrete_Random
   (Delay_Interval);
   use Random_Delay;
   G : Generator;

   task Producer;

   task Consumer;

   task body Producer is
      Next : Time;
   begin
      Next := Clock;
      for I in 1..N loop
         -- Write to X
         S.Wait;
         X := I;
         S.Signal;
         --Next 'Release' in 50..250ms
         Next := Next + Milliseconds(Random(G));
         Put_Line(Integer'Image(X));
         delay until Next;
      end loop;
   end;

   task body Consumer is
      Next : Time;
   begin
      Next := Clock;
      for I in 1..N loop
         -- Read from X
         S.Wait;
         Put_Line(Integer'Image(X));
         S.Signal;
         Next := Next + Milliseconds(Random(G));
         delay until Next;
      end loop;
   end;

begin -- main task


   null;
end ProducerConsumer_sem;

我建议您整理一下源代码(适当缩进,没有残留的注释,使用Ada风格的标识符),并在实现之前呈现包规范。 - Jacob Sparre Andersen
@JacobSparreAndersen 我已经编辑了,感谢你的提醒。 - Adam
你说“我得到了以下输出”,但它并不存在。 - Simon Wright
@SimonWright 输出在描述部分,数值从1到40,如果您查看黄色区域的部分,可能是我放错了位置,抱歉。 - Adam
哦,我以为那是问题的一部分。 - Simon Wright
实际上,我不确定输出是否正确,请查看一下输出是否有意义。 - Adam
1个回答

4
在macOS上,使用FSF GCC 7.1.0和GNAT GPL 2017,我将您的Put_Line更改为Put,并得到了与问题中所述几乎相同的答案。
问题要求创建Semaphore.ads.adb。这可以在Windows上运行,并且可能在macOS上运行,但在Linux上不起作用,因为GNAT的文件命名约定(请参见this的末尾;养成使用小写文件名的习惯是个好主意)。
如果您想确保只有一个任务可以同时访问X,我认为您的WaitSignal调用没有太大问题,尽管当我在Producer的开头放置delay 0.1时,第一个输出的值是151619216(因为X未初始化)。然而!如果目的是一次传递一个更新到X(如生产者/消费者名称所暗示的那样),则应该
  • 将信号量初始化为0(最大值为1)。这将使它成为二进制信号量。
  • Consumer中,仅使用Wait(即删除Signal
  • Producer中,仅使用Signal(即删除Wait)。此外,删除Put以避免混淆!

谢谢你的回答,不幸的是老师要我使用 gnatmake 命令在 Linux 上编译和运行它,我对 Ada 不太熟悉。但是你提到的这一点非常有趣,如果在 Linux 上无法工作,你的意思是无法编译还是无法得出正确的输出? 我按照老师的指示在 Ubuntu 16 上使用命令 gnatmake producercomsumer.adb./producerconsumer 运行这个程序,在黄色部分给出了输出,但不确定输出是否正确或者是什么。 - Adam
如果我将计数器初始化为0并将最大值设置为40,这是否意味着它是计数信号量? - Adam
如果您将文件命名为 semaphore.ads.adb,而不是 Semaphore,它会正常工作(好吧,这么说吧,如果您使用大写名称使其正常工作,我感到惊讶)。至于“正确” - 它应该是可靠的生产者-消费者,但是39被生产而不被消耗!此外,40被生产了,但被消耗了两次。所以我猜不是。40 的 Max 将被计数。 - Simon Wright
你是完全正确的。那是教师在作业文本中的一个笔误,也影响了我。但是他们交给我的框架里所有的包都是小写字母(semaphore.ads),很抱歉。 - Adam
有什么办法可以使用计数信号量使其更可靠吗?我应该尝试为不同的“max”值和“count”值进行初始化测试吗? - Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接