我想创建一个从文件读取几分钟的任务,而主线程执行其他操作。但是我希望主线程能够轮询任务以查看它是否为“忙”(布尔值)而不会阻塞主线程。
我在这里有一个幼稚的尝试,它确实起作用,但是它使Busy标志完全暴露出来,可以由主线程随意切换(这是不安全的)...
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
task type Non_Blocking_Reader_Task (Busy : access Boolean) is
entry Read (Destination : in Natural);
end Non_Blocking_Reader_Task;
task body Non_Blocking_Reader_Task is
begin
loop
select
when not Busy.all =>
accept Read (Destination : in Natural) do
Busy.all := True;
end Read;
for i in 1 .. 50 loop
Put ("."); -- pretend to do something useful
delay 0.1; -- while wasting time
end loop;
Busy.all := False;
end select;
end loop;
end Non_Blocking_Reader_Task;
Reader_Busy_Volatile : aliased Boolean;
Reader : Non_Blocking_Reader_Task (Reader_Busy_Volatile'Access);
begin
Put_Line (Reader_Busy_Volatile'Image);
Reader.Read (123);
for i in 1 .. 15 loop
Put_Line (Reader_Busy_Volatile'Image);
delay 0.5;
end loop;
abort Reader;
end Main;
Run Code Online (Sandbox Code Playgroud)
我的第二个想法是创建一个a protected type并在其中隐藏标志和任务,但这是语言所不允许的。
题
如何创建受保护的“任务繁忙”标志,该标志可以从主线程为只读状态,并且可以从任务中读/写(不会导致主线程阻塞)?
编辑:
解决方案!
我根据@flyx的令人鼓舞的建议修订的(有效的)解决方案:)
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
task type Reader_Task is
entry Read (Destination : in Natural);
entry Join;
entry Ready;
end Reader_Task;
task body Reader_Task is
Dest : Natural;
begin
loop
select
accept Read (Destination : in Natural) do
Dest := Destination;
end Read;
-- we only get here after a Read has been received.
for i in 1 .. 5 loop
Put ("."); -- pretend to do something useful
delay 1.0; -- while wasting time
end loop;
or
accept Join;
or
accept Ready;
or
terminate;
end select;
end loop;
end Reader_Task;
Reader : Reader_Task;
begin
-- first call will not block.
Reader.Read (123);
Put_Line ("MAIN: Reading in progress on second thread");
for i in 1 .. 12 loop
select
-- NON-BLOCKING CALL!
Reader.Ready; -- test if task is busy
Put_Line ("MAIN: NON-BLOCKING CALL SUCCEEDED -- TASK IS NOT BUSY");
else
Put_Line ("MAIN: NON-BLOCKING CALL FAILED -- TASK IS BUSY");
end select;
delay 1.0;
end loop;
Put_Line ("Main: Waiting for Reader (BLOCKING CALL)...");
Reader.Join;
Put_Line ("Main: all finished!");
end Main;
Run Code Online (Sandbox Code Playgroud)
I've added two more entries to the task: Join and Ready which are basically the same but for the names. Join reminds me to do a blocking call to it, and Ready indicates that a non-blocking call is suitable for testing task availability. I've done this because there are times when I want to know if the previous run of Read() has finished without firing off a new one. This lets me do this neatly and all without any discrete flags at all! Awesome.
在Ada中,呼叫者决定进入呼叫是否正在阻塞。您不应尝试在任务内部实施用于检查此代码。
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
task type Reader_Task is
entry Read (Destination : in Natural);
end Reader_Task;
task body Reader_Task is
begin
loop
select
accept Read (Destination : in Natural) do
null;
-- store Destination (?)
end Read;
or
-- allow task to be terminated while waiting
terminate;
end select;
-- we only get here after a Read has been received.
for i in 1 .. 50 loop
Put ("."); -- pretend to do something useful
delay 0.1; -- while wasting time
end loop;
end loop;
end Reader_Task;
Reader : Reader_Task;
begin
-- first call will not block.
Reader.Read (123);
for i in 1 .. 15 loop
-- check whether Read can be called immediately and if yes,
-- call it.
select
Reader.Read (456);
else
-- Read is not available, do something else.
null;
end select;
delay 0.5;
end loop;
-- don't call abort; let Reader finish its current task.
-- Reader will be terminated once it waits on the terminate alternative
-- since the parent is finished.
end Main;
Run Code Online (Sandbox Code Playgroud)
该select … else … end select结构是Ada进行非阻塞调用的方式。您不使用标志来表示任务已准备好接收条目调用,因为这种状态可能会在标志的查询和条目的实际调用之间改变。select旨在避免此问题。