如何冻结任务?
我的意思是,如果我有任务
task body My_Task is
begin
accept Start;
loop
Put ("1");
Put ("2");
Put ("3");
...
Put ("n");
end loop;
end My_Task;
有没有办法可以“冻结”当前状态的任务?例如,如果执行完成执行Put ("2");
,我怎么能冻结它,以后我可以把它继续?我想要从任务之外以及从外部开始冻结,命令它继续。
更新
如果我有像
这样的规范,我可以肯定实现type State_Type is
(RUN,
FROZEN);
task type My_Task (State : State_Type) is
entry Start;
end My_Task;
身体
task body My_Task is
begin
accept Start;
loop
Put ("1");
Put ("2");
Put ("3");
...
Put ("n");
loop
if State = RUN then exit; end if;
end loop;
end loop;
end My_Task;
但事实并非如此,因为我不得不等待第n个Put指令行(即,任务实际上不会被冻结,因为内部循环将会运行)。
答案 0 :(得分:5)
您可以使用“选择性接受”来允许您的任务被外部呼叫者(另一个任务或主程序)中断。你不能(轻松地)在执行中的任意点中断任务;相反,任务本身需要确定何时接受入场呼叫。
所以你可能想要替换你的序列
Put("1");
Put("2");
Put("3");
...
Put("n");
通过每次迭代调用Put
一次的循环,每次都执行select
语句。
这是我刚刚拼凑的演示程序。计数器每秒递增一次(大约)。如果Running
为真,则打印计数器值;否则循环静默进行。主程序每3秒钟调用Pause和Resume。
else
语句中的select
子句会导致它继续执行,如果两个条目都没有被调用。
with Ada.Text_IO; use Ada.Text_IO;
procedure T is
task Looper is
entry Pause;
entry Resume;
end Looper;
task body Looper is
Running: Boolean := True;
Count: Integer := 0;
begin
loop
select
accept Pause do
Running := False;
end;
or
accept Resume do
Running := True;
end;
else
null;
end select;
delay 1.0;
if Running then
Put_Line("Running, Count = " & Integer'Image(Count));
end if;
Count := Count + 1;
end loop;
end Looper;
begin -- T
loop
delay 3.0;
Looper.Pause;
delay 3.0;
Looper.Resume;
end loop;
end T;
可能有比这更优雅的方法。自从我真正使用过Ada以来已经很久了。
答案 1 :(得分:4)
delay
或delay until
个保留字。
你要指定一个你想要恢复执行的时间,然后说:
delay <time>
或
delay until <time>
我不记得确切的细节,但这里是Ada95规范示例:http://www.adaic.org/resources/add_content/docs/95style/html/sec_6/6-1-7.html
答案 2 :(得分:4)
Ada语言背后的设计意图的一个方面是行为是明确的。一个突然发现自己在一个任意点被“冻结”的执行任务没有任何明显的理由 - 任务中没有任何东西会导致这种行为 - 最多会令人不安。如果任务的设计不允许意外的,任意执行暂停,那么可能是一个错误。 (我知道操作系统时间切片在执行程序时强加了这种行为,但这是操作系统的功能,而不是编程语言。)
简而言之,我会严肃质疑依赖外部强制执行任务暂停的设计方法。任务应该知道它自己的状态,因此知道什么时候可以安全地暂停,以及不变量必须保持暂停以确保正确恢复。
答案 3 :(得分:2)
要获得正确中断put("x")
序列所需的粒度,您应该将其编写为将其状态保存在task my_task
内的过程(知道哪个原子指令已被执行)。
然后我认为您可以在State_Type
上使用guard,并且只在未冻结时执行。当然这不会停止任务,它只会保护put("x")
序列不被执行(我想这就是你想要的!)
答案 4 :(得分:1)
如果您有一个特定的行,您总是希望您的任务等待,那么解决方案相当简单:只需在其中添加accept
语句即可。任务将挂起,直到其他任务调用该条目。
如果您想要概括这个想法(因此可以使多个任务等待,或者多个不同的任务可以安全地执行释放操作),那么将State
变量封装在受保护对象中可能会更好(正如NWS建议的那样)。受保护对象的特性被专门用于语言,以便为这样的任务创建同步化对象。
答案 5 :(得分:1)
您在回复其中一条评论时提到,由于某些共享资源,您需要这样做。那么,答案似乎是您希望将该共享资源包装在受保护对象中,并从该任务中调用该受保护对象的过程/条目;当一个任务排队(或执行)一个受保护的对象调用时,它将被置于“阻塞”状态,直到它返回,从而“冻结”进一步的处理。
With
Ada.Text_IO,
Ada.Strings.Fixed,
Ada.Numerics.Float_Random
;
with ada.Integer_Text_IO;
with Ada.Strings;
Use Ada.Numerics.Float_Random;
Procedure Experiment is
Task Type Testing( Text : Not Null Access String ) is
end Testing;
protected Resource is
procedure Write( Input : In String );
private
end Resource;
Task Body Testing is
G : Ada.Numerics.Float_Random.Generator;
D : Duration := Duration( Random(G) );
Begin
delay D;
Resource.Write( Text.ALL );
End Testing;
protected body Resource is
procedure Write( Input : In String ) is
begin
Ada.Text_IO.Put_Line( Input );
end Write;
end Resource;
Function Make(Input : in String) Return Access Testing is
begin
Return Result : Access Testing:= New Testing( New String'(Input) );
end Make;
TaskList : Array (Positive Range <>) of Access Testing:=
( Make("Anger"), Make("Rage"), Make("Joy"), Make("Contentment") );
Use Ada.Text_IO;
Begin
Put_Line( "Terminating." );
End Experiment;
注意:put_line的使用在技术上不是线程安全的,[IIRC]可能会阻塞......所以虽然上面应该永远不会死锁,但实际上并没有保证。
答案 6 :(得分:0)
我还得到了Anh Vo in comp.lang.ada (Freezing a task)的答案,其中包括Rendezvous以及任务终止。通过一些调整,它得到:
task type My_Task is
entry Start;
entry Pause;
entry Quit;
entry Stop;
end My_Task;
-- (...)
task body My_Task is
begin
Outer_Loop : loop
select
accept Start;
Main_Loop : loop
select
accept Pause;
select
accept Quit;
or
accept Stop;
exit Outer_Loop;
or
terminate;
end select;
or
accept Stop;
exit Outer_Loop;
else
Put ("Main code here");
delay MYCYCLE; -- MYCYCLE is 2.0; (whatever)
end select;
end loop Main_Loop;
or
terminate;
end select;
end loop Outer_Loop;
end My_Task;
我承认我认为这个解决方案非常优雅。