Ada - 如何实现一个允许主线程轮询的异步任务?
Ada - How to implement an asynchronous task that allows the main thread to poll it?
我想创建一个任务,在主线程做其他事情的同时从文件中读取几分钟。但我希望主线程能够在不阻塞主线程的情况下轮询任务以查看它是否为 "Busy"(布尔值)。
我在这里做了一个幼稚的尝试,它确实有效,但它让 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;
我的第二个想法是创建一个 protected type
并将标志和任务隐藏在其中,但语言不允许这样做。
问题
我如何创建一个受保护的 "task is busy" 标志,它可以从主线程只读并且 read/write 从任务(不会导致主线程阻塞)?
编辑:
The solution!
我根据@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;
我在任务中又添加了两个条目:Join
和 Ready
,它们基本上相同,只是名称不同。 Join提醒我对其进行阻塞调用,Ready表示非阻塞调用适合测试任务可用性。我这样做是因为有时我想知道 Read() 的前一个 运行 是否已完成而不触发新的。这让我可以干净利落地完成这一切,完全没有任何离散的标志!厉害了。
在 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;
select … else … end select
结构是 Ada 进行非阻塞调用的方式。您不使用标志来表示任务已准备好接收条目调用,因为此状态可能会在标志查询和条目的实际调用之间发生变化。 select
旨在避免此问题。
我想创建一个任务,在主线程做其他事情的同时从文件中读取几分钟。但我希望主线程能够在不阻塞主线程的情况下轮询任务以查看它是否为 "Busy"(布尔值)。
我在这里做了一个幼稚的尝试,它确实有效,但它让 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;
我的第二个想法是创建一个 protected type
并将标志和任务隐藏在其中,但语言不允许这样做。
问题
我如何创建一个受保护的 "task is busy" 标志,它可以从主线程只读并且 read/write 从任务(不会导致主线程阻塞)?
编辑:
The solution!
我根据@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;
我在任务中又添加了两个条目:Join
和 Ready
,它们基本上相同,只是名称不同。 Join提醒我对其进行阻塞调用,Ready表示非阻塞调用适合测试任务可用性。我这样做是因为有时我想知道 Read() 的前一个 运行 是否已完成而不触发新的。这让我可以干净利落地完成这一切,完全没有任何离散的标志!厉害了。
在 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;
select … else … end select
结构是 Ada 进行非阻塞调用的方式。您不使用标志来表示任务已准备好接收条目调用,因为此状态可能会在标志查询和条目的实际调用之间发生变化。 select
旨在避免此问题。