假设以下(服务器)任务规范:
task type Server_Task is
entry E1(I: Integer);
entry E2;
entry E3;
end Server_Task;
(虚拟)实现:
task body Server_Task is
begin
loop
select
accept E1(I: Integer) do
-- statements inside E1 using I
-- statements inside E1 using I
-- statements inside E1 using I
null;
end;
or
accept E2 do
null;
end;
or
accept E3 do
null;
end;
end select;
end loop;
end Server_Task;
根据我的理解,如果客户端任务对(例如)E1 进行入口调用,则在服务器任务再次循环并准备接受另一个入口调用之前,将执行 E1
接受块内的
all语句。如果接受块的
end
之后有更多语句,则同样如此,因此,在任务再次与调用任务进行 randevouz 之前,需要运行 all 这些语句。
如果这个假设是正确的,我想知道上面的实现和下面的实现之间的行为差异是什么:
task body Server_Task is
Temp: Integer;
begin
loop
select
accept E1(I: Integer) do
Temp := I;
end;
-- statements outside E1 using Temp
-- statements outside E1 using Temp
-- statements outside E1 using Temp
or
accept E2 do
null;
end;
or
accept E3 do
null;
end;
end select;
end loop;
end Server_Task;
如果语句 outside
E1
进行阻塞调用,因此服务器任务被挂起,因此这些语句必须以某种方式与任务客户端进行的任何其他条目调用竞争,是否会有差异? (尽管如果只使用一个“线程”来实现任务,这没有多大意义?)
为了便于论证,假设客户端代码如下:
ST: Server_Task;
task body Client_Task is
begin
select
ST.E2;
else
-- do something else
null;
end select;
null;
end Client_Task;
ARM 中是否对此行为进行了详细说明? - 谢谢
最大的区别在于,服务器和客户端都被锁定,等待接受语句完成,然后再继续。您是正确的,在这两种情况下,服务器基本上仍然运行相同的代码,无论位置如何。但在第一种情况下,客户端正在等待这些语句完成,而在第二种情况下,客户端可以继续执行其他操作,而接受后的这些语句则继续执行。
这两种情况各有利弊。在第一种情况下,您有能力执行不受客户端数据竞争影响的操作,而在第二种情况下,如果这些语句调整全局/共享数据,那么您将面临数据竞争,因为客户端和服务器都可以操作就该数据而言。
也就是说,在大多数情况下我更喜欢第二种情况,只有当我需要保护数据时才会回到第一种情况。主要原因是它可以让客户腾出时间去做其他工作,而不是等待。
accept E1(I: Integer) do
-- statements inside E1 using I
-- statements inside E1 using I
-- statements inside E1 using I
null;
end; -- Client is free
对
accept E1(I: Integer) do
null;
end;
-- Client is free everything after is potentially unsafe
-- for client, but the client can do other things quicker
-- statements inside E1 using I
-- statements inside E1 using I
-- statements inside E1 using I
第三部分: 该选择基本上只是检查集合点是否开始。一旦集合开始,就客户而言,您就在那里。 “其他”部分仅在等待集合发生时适用。
对于集合点“之后”的阻塞语句,这些语句将延迟或阻止下一个集合点发生,直到它们解决为止。它们不会直接影响客户端,除非客户端稍后与同一任务或两个任务共享的受保护对象条目进行另一个交会。但此时,将选择 select 语句中的“ELSE”,因为客户端无法重新进入集合点,直到服务器完成阻塞任务并返回到接受语句代码。
这一切都在手臂上有详细说明。我不知道这些部分,但在目录中查找任务部分或在索引中查找 select 语句。
好吧,这里有几个语言功能在发挥作用……也许解释事情如何运作的最好方法是发展一个小的“协议”——首先让我们有一个接受一个
A
、两个 B
的系统s 和 C
:
Task Example_1 is
Entry A;
Entry B;
Entry C;
End Example_1;
Task Body Example_1 is
Begin
-- Protocol accept one A, followed by 2 B, finished with a C.
Accept A;
Accept B;
Accept B;
Accept C;
End Example_1;
太好了,我们有一个任务按顺序接收消息
A
、B
、B
、C
并终止。现在,稍微复杂化协议,让我们将协议设为 A
,后跟 B
或 C
,然后以 A
终止。
Task Example_2 is
Entry A;
Entry B;
Entry C;
End Example_2;
Task Body Example_2 is
Begin
-- Protocol: A, (B or C), A.
Accept A;
Select
Accept B;
or
Accept C;
End Select;
Accept A;
End Example_2;
现在让事情变得更复杂一点:假设任务是一个解析器,
A
是提交文本,B
是检索标记和未使用文本的长度,C
正在返回未使用的内容文本,全部循环直到完成:
Task Example_3 is
Entry A(Text : in String);
Entry B(List : out Token_List; Remainder : out Natural);
Entry C(Text : out String);
Entry Finish;
End Example_3;
Task Body Example_3 is
Temp : String_Holder.Holder:= String_Holder.Empty_Holder;
Result : Token_List:= Empty_List;
Finished : Boolean:= False;
Begin
Parser_Loop:
Loop
-- Protocol: Loop of (A, B, C) or finished.
Select
Accept A(Text : in String) do
Temp:= To_Holder( Text )
End A;
-- Processing TEMP here.
Accept B(List : out Token_List; Remainder : Out Natural) do
List := Result;
Remainder:= Temp.Element'Length;
End B;
Accept C(Text : out String) do
Text:= Temp.Element;
End C;
or
Accept Finish do
Finished:= True;
End Finish;
End Select;
Exit Parser_Loop when Finished;
End loop Parser_Loop;
End Example_3;
并且要掌握理解
entry
、accept
和 select
的大部分基础知识——唯一要记住的重要事项是 ACCEPT
和 DO
之间的 END
中的内容是集合点,因此调用者和被调用者都是同步的...如果您希望并行度增加,请务必在集合点之外进行实际处理。