Ada-如何实现允许主线程轮询的异步任务?

我想创建一个从文件读取几分钟的任务,而主线程执行其他操作。但是我希望主线程能够轮询任务以查看它是否为“忙”(布尔值)而不会阻塞主线程。

我在这里有一个幼稚的尝试,它确实有效,但是它使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并在其中隐藏标志和任务,但这是语言所不允许的。

问题

我如何创建一个受保护的“任务繁忙”标志,该标志可以从主线程为只读状态,并且可以从任务中读/写(不会导致主线程阻塞)?


编辑:

  

解决方案!

我根据@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;

我为任务添加了另外两个条目:JoinReady,它们的名称基本上相同。 Join提醒我对其进行阻塞调用,而Ready表示非阻塞调用适合测试任务可用性。之所以这样做,是因为有时我想知道Read()的上一次运行是否已完成而又没有触发新的运行。这使我可以整齐地执行此操作,并且完全没有任何离散标志!很棒。

xzpxiao 回答:Ada-如何实现允许主线程轮询的异步任务?

在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旨在避免此问题。

本文链接:https://www.f2er.com/3124394.html

大家都在问