Parallel processing (Ada)从具有2个任务的基本受保护有界缓冲区获取时出现奇怪的阻塞
在Ada中,这是一个非常基本的受保护的有界缓冲区,就像它在任何地方,甚至在教科书中所显示的一样。(这是一个更大的问题的一部分,但我将代码简化到了最低限度,它再现了行为)。 如果我有一个任务给它喂食,而“主体”正在阅读它,那么它似乎工作得很好。但是如果我使用两个任务——推杆和吸气,它会在第一个Get上阻塞,如下所示Parallel processing (Ada)从具有2个任务的基本受保护有界缓冲区获取时出现奇怪的阻塞,parallel-processing,ada,protected,Parallel Processing,Ada,Protected,在Ada中,这是一个非常基本的受保护的有界缓冲区,就像它在任何地方,甚至在教科书中所显示的一样。(这是一个更大的问题的一部分,但我将代码简化到了最低限度,它再现了行为)。 如果我有一个任务给它喂食,而“主体”正在阅读它,那么它似乎工作得很好。但是如果我使用两个任务——推杆和吸气,它会在第一个Get上阻塞,如下所示 with Ada.Text_IO;use Ada.Text_IO; procedure test_buffer is maxItems : constant Positive
with Ada.Text_IO;use Ada.Text_IO;
procedure test_buffer is
maxItems : constant Positive := 10;
type Index is mod maxItems;
maxCount : constant Index := 9;
type ItemArray is array(Index) of Integer;
protected Buffer is
entry Put(X : in Integer);
entry Get(X : out Integer);
private
First, Last, Count : Index := 0;
buf : ItemArray;
end;
protected body Buffer is
entry Put(X : in Integer) when Last - First < maxCount is
begin
Put_Line("Put X="&X'Img & "; First="&First'Img&", Last="&Last'Img&", Count="&Count'Img);
buf(Last) := X;
Last := Last + 1;
Count := Count + 1;
end;
--
entry Get(X : out Integer) when Last - First > 0 is
begin
Put_Line("Get X="&X'Img & "; First="&First'Img&", Last="&Last'Img&", Count="&Count'Img);
X := buf(First);
First := First + 1;
Count := Count - 1;
end;
end;
task Putter;
task body Putter is
begin
Put_Line("Putter started");
for i in 0 ..25 loop
Buffer.Put(i);
end loop;
end;
task Getter;
task body Getter is
X : Integer;
begin
Put_Line("Getter started");
loop
Put_Line("requesting X..");
Buffer.Get(X);
Put_Line("got X="&X'Img);
end loop;
end;
-- X : Integer;
begin
-- loop
-- Buffer.Get(X);
-- Put_Line("got X="&X'Img);
-- end loop;
Null;
end test_buffer;
正如您所看到的,Get永远不会被执行,这可能是因为没有重新评估屏障。但是,如果我取消对主体的注释并注释掉任务(或者甚至只是取消对主体的注释,那么我将获得并发读取),那么所有读取和写入操作都将正常进行
我似乎找不到任何规则会在Get上拖延障碍(第二个任务是外部的,所以不,它不是对受保护对象的内部调用的no重新评估)
我是否遗漏了一些明显的东西,或者这是一个bug?ARM第9.5.1节指出,在受保护的操作期间,调用可能阻塞的操作是一个有界错误。进一步,它用某些语言定义的子程序可能阻塞的语句阐明了这一点。特别是,语言定义的输入输出包的子程序(隐式或显式)可能会阻塞文件。
受保护项中的Put_Line过程调用可能会阻塞,应将其从项中删除。ARM第9.5.1节指出,在受保护操作期间,调用可能阻塞的操作是有界错误。进一步,它用某些语言定义的子程序可能阻塞的语句阐明了这一点。特别是,语言定义的输入输出包的子程序(隐式或显式)可能会阻塞文件。
受保护条目中的Put_Line过程调用可能会阻塞,应该从条目中删除。对我来说很有效(无可否认,这并不能证明没有bug)。。。Debian Stretch中的gcc-6(FSF)。你的操作系统和编译器版本是什么?在这里,这两个任务都是在第一次Put之前开始的,这可能会有所不同。对我来说也适用(64位Debian/Jessie上的GNAT 4.9.2)。但是您应该知道,I/O调用是“潜在的阻塞”,因此在受保护的操作中是非法的;我不能在这里工作,也不能在另一台计算机上工作。更奇怪的是。看看这是多么标准,我实例化了GNAT.Bounded_Buffers(它的主体中有几乎完全相同的代码),它挂起得更快,插入和删除(在我的代码中放置和获取)都挂起了。。像这样:呃,很明显,它是在回车时提交的,很抱歉评论被打断了。无论如何,下面是示例输出:$./test\u缓冲区Getter开始请求X。。Putter启动了X=0^C系统(两个)nrun Gentoo Linux,大约在一个月内更新。相关Ada:gnat-gpl-2017(gcc-6.3.0)。原始(我的)代码至少会一直运行,直到PUT被缓冲区大小阻塞,甚至偶尔进行1-2次读取。所以,在gnat realtime中看起来更像是一种竞争条件。对我来说很有效(不可否认,这并不能证明没有bug)。。。Debian Stretch中的gcc-6(FSF)。你的操作系统和编译器版本是什么?在这里,这两个任务都是在第一次Put之前开始的,这可能会有所不同。对我来说也适用(64位Debian/Jessie上的GNAT 4.9.2)。但是您应该知道,I/O调用是“潜在的阻塞”,因此在受保护的操作中是非法的;我不能在这里工作,也不能在另一台计算机上工作。更奇怪的是。看看这是多么标准,我实例化了GNAT.Bounded_Buffers(它的主体中有几乎完全相同的代码),它挂起得更快,插入和删除(在我的代码中放置和获取)都挂起了。。像这样:呃,很明显,它是在回车时提交的,很抱歉评论被打断了。无论如何,下面是示例输出:$./test\u缓冲区Getter开始请求X。。Putter启动了X=0^C系统(两个)nrun Gentoo Linux,大约在一个月内更新。相关Ada:gnat-gpl-2017(gcc-6.3.0)。原始(我的)代码至少会一直运行,直到PUT被缓冲区大小阻塞,甚至偶尔进行1-2次读取。所以,看起来更像是gnat realtime中的一个竞赛条件……好吧,他们一开始就不在那里(当它阻塞时)。我添加它们是为了追踪发生了什么。。但是这个问题已经解决了——首先,在其他一些例子中,它是一个中止(而不是阻塞)问题(这里不是,这个是Null),但在“大项目”中仍然阻塞,我怀疑是接口和类范围的调用。但是现在它也解决了,尽管以一种相当神秘的方式——它刚刚开始工作。。(我只是运行了一个diff,没有任何必要的更改),但可能确实是在周二阻止了Text_IO,而不是一周中的其他几天:)。无论如何,感谢所有的输入!好吧,他们一开始不在那里(当它阻塞时)。我添加它们是为了追踪发生了什么。。但是这个问题已经解决了——首先,在其他一些例子中,它是一个中止(而不是阻塞)问题(这里不是,这个是Null),但在“大项目”中仍然阻塞,我怀疑是接口和类范围的调用。但是现在它也解决了,尽管以一种相当神秘的方式——它刚刚开始工作。。(我只是运行了一个diff,没有任何必要的更改),但可能确实是在周二阻止了Text_IO,而不是一周中的其他几天:)。无论如何,感谢所有的输入!
$ ./test_buffer
Putter started
Put X= 0; First= 0, Last= 0, Count= 0
Put X= 1; First= 0, Last= 1, Count= 1
Put X= 2; First= 0, Last= 2, Count= 2
Put X= 3; First= 0, Last= 3, Count= 3
Getter started
Put X= 4; First= 0, Last= 4, Count= 4
Put X= 5; First= 0, Last= 5, Count= 5
Put X= 6; First= 0, Last= 6, Count= 6
Put X= 7; First= 0, Last= 7, Count= 7
Put X= 8; First= 0, Last= 8, Count= 8
^C