冻结 Ada 任务

发布于 2024-12-16 20:19:43 字数 812 浏览 1 评论 0原文

如何冻结任务?

我的意思是,如果我有一个任务

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 指令行(即,任务实际上不会被冻结,因为内部循环将会运行)。

How can I freeze a task?

I mean, if I have a task

task body My_Task is
begin 
  accept Start;
  loop
    Put ("1");
    Put ("2");
    Put ("3");
    ...
    Put ("n");
  end loop;
end My_Task;

is there a way that I can "freeze" the task in its current state? If, for instance, the execution finished executing Put ("2");, how can I freeze it and later I can turn it to continue? I want to provoque a freeze from outside the task, and also from outside, order it to continue.

Update

I could sure implement, if I had the spec like

type State_Type is
  (RUN,
   FROZEN);

task type My_Task (State : State_Type) is
   entry Start;
end My_Task;

the body

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;

but it would not be the case because I had to wait for the nth Put instruction line (i.e., the task would not be actually frozen, because the inside loop would be running).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

原谅过去的我 2024-12-23 20:19:43

您可以使用“选择性接受”来允许您的任务被外部调用者(另一个任务或主程序)中断。您不能(轻易)在任务执行过程中的任意时刻中断任务;相反,任务本身需要确定何时接受进入调用。

因此,您可能希望

Put("1");
Put("2");
Put("3");
...
Put("n");

用一个循环替换您的序列,该循环在每次迭代时调用一次 Put ,并每次都执行一个 select 语句。

这是我刚刚编写的一个演示程序。计数器每秒(大约)递增一次。如果 Running 为 true,则打印计数器值;否则循环会默默地进行。主程序每 3 秒交替调用 Pause 和 Resume。

如果两个条目都没有被调用,select 语句中的 else 子句会导致它继续执行。

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了。

You can use a "selective accept" to allow your task to be interrupted by an external caller (another task or the main program). You can't (easily) interrupt a task at an arbitrary point in its execution; instead, the task itself needs to determine when it will accept entry calls.

So you'll probably want to replace your sequence

Put("1");
Put("2");
Put("3");
...
Put("n");

by a loop that calls Put once on each iteration, with a select statement that's executed each time.

Here's a demo program that I just threw together. The counter increments once per second (approximately). The counter value is printed if Running is true; otherwise the loop proceeds silently. The main program alternatively calls Pause and Resume every 3 seconds.

The else clause in the select statement cause it to continue executing if neither entry has been called.

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;

There may be a more elegant approach than this. It's been a long time since I've really used Ada.

与往事干杯 2024-12-23 20:19:43

哇,好久没看到艾达的问题了。无论如何,每当您需要暂停任务时,您都可以使用“延迟”或“延迟直到”保留字。

您可以指定要恢复执行的时间,然后说:

延迟<时间>

延迟直到<时间>

我不记得确切的细节,但是这是 Ada95 规范示例: http://www.adaic.org/resources/add_content/docs/95style/html/sec_6/6-1-7.html

Wow, haven't seen an Ada question in awhile. Anyway whenever you need to pause a task you use the delay or delay until reserved words.

you would specify a time you want to resume execution, then say:

delay <time>

or

delay until <time>

I don't remember the exact details, but here is the Ada95 specification example: http://www.adaic.org/resources/add_content/docs/95style/html/sec_6/6-1-7.html

虫児飞 2024-12-23 20:19:43

Ada 语言的设计意图之一是行为是明确的。一项正在执行的任务突然发现自己由于没有明显原因而“冻结”在任意点(任务中没有任何内容会导致该行为),充其量只会令人不安。如果任务的设计不允许意外的、任意的执行暂停,则可能是一个错误。 (我知道操作系统时间切片将这种行为强加于执行程序,但这是操作系统的功能,而不是编程语言的功能。)

简而言之,我会严重质疑依赖外部强加的任务暂停的设计方法。任务应该知道它自己的状态,从而知道何时可以安全地暂停,以及暂停时必须保持哪些不变量以确保正确恢复。

One aspect of the intent of the design underlying the Ada language is that behavior be explicit. An executing task that suddenly found itself "frozen" at an arbitrary point for no readily discernible reason--there being nothing in the task that would lead to that behavior--would be disconcerting at best. And possibly a bug if the task was not designed to allow for unexpected, arbitrary execution suspension. (I know OS time-slicing imposes this behavior on executing programs, but that's a function of the OS, not the programming language.)

In short, I would seriously question a design approach that relied on externally imposed task suspension. A task should know it's own state, thus know when it's safe to pause, and what invariants have to hold for pausing in order to ensure a proper resumption.

空心空情空意 2024-12-23 20:19:43

为了获得正确中断 put("x") 序列所需的粒度,您应该将其编写为一个过程,将其状态保存在 task my_task 中(知道哪个原子指令已被执行)。

那么我想你可以在你的 State_Typeguard code> 并且仅在未冻结时执行。当然,这不会停止任务,它只会保护 put("x") 序列不被执行(我认为这就是你想要的!)

To get the granularity you need to correctly interrupt your put("x") sequence, you should write that as a procedure that saves its state within task my_task (knowing which atomic instruction has been executed).

Then I think you could then use a guard on your State_Type and only execute when not frozen. of course this will not stop the task, it will simply protect the put("x") sequence from being executed (which is what you want, i think!)

佼人 2024-12-23 20:19:43

如果您总是希望任务在某一特定行等待,那么解决方案相当简单:只需在此处放置一条 accept 语句即可。该任务将挂起,直到其他任务调用该条目。

如果你想推广这个想法(这样可以让多个任务等待,或者多个不同的任务可以安全地执行释放操作),最好将你的 State 变量封装在受保护的对象中(正如国家气象局建议的那样)。受保护对象的功能被专门放入语言中,以方便为此类任务创建同步对象。

If you have a specific line where you would always like your task to wait, the solution is fairly easy: Just put an accept statement there. The task will hang until some other task calls that entry.

If you want to generalize this idea (so multiple tasks can be made to wait, or multiple different tasks can safely perform the release action), it might be better to encapsulate your State variable inside a protected object (as NWS suggested). The feature of protected objects was put in the language specifically to facilitate the creation of synchornization objects for tasks like this.

故事和酒 2024-12-23 20:19:43

您在回复其中一条评论时提到,您希望这样做是因为有一些共享资源。那么,答案似乎是您想要将该共享资源包装在受保护对象中,并从任务中调用该受保护对象的过程/条目;当任务在受保护对象调用上排队(或执行)时,它会进入“阻塞”状态,直到它返回,从而“冻结”进一步的处理。

编辑:添加了代码示例。


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] 可能会阻塞......因此,虽然上述内容永远不会死锁,但实际上并不能保证。

You mentioned, in response to one of the comments, that you want this because of some shared resources. The answer, then, seems to be that you want to wrap that shared-resource in a protected-object and call that protected-object's procedures/entries from the task; while a task is queued up on (or executing) a protected-object call it is put into the 'blocked' state until it returns thereby 'freezing' the further processing.

Edit: Added a code example.


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;

Note: The use of put_line is technically not thread-safe, and [IIRC] potentially blocking... so while the above should never deadlock such is not actually guaranteed.

心头的小情儿 2024-12-23 20:19:43

我还从 Anh Vo in comp.lang.ada 得到了答案(冻结任务),其中还包括带有任务终止的 Rendevouz。经过一些调整,它得到:

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;

我承认我认为这个解决方案非常优雅。

I got also an answer from Anh Vo in comp.lang.ada (Freezing a task), which includes the Rendevouz also with task termination. With some adjustments, it gets:

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;

I confess I think this solution is very elegant.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文