Замораживание задачи Ады - PullRequest
       38

Замораживание задачи Ады

6 голосов
/ 17 ноября 2011

Как я могу заморозить задачу?

Я имею в виду, если у меня есть задание

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 (т. Е. Задача фактически не была бы заморожена, потому что выполнялся внутренний цикл).

Ответы [ 7 ]

5 голосов
/ 17 ноября 2011

Вы можете использовать «выборочное принятие», чтобы позволить вашей задаче быть прерванной внешним абонентом (другая задача или основная программа).Вы не можете (легко) прервать задачу в произвольный момент ее выполнения;вместо этого сама задача должна определить, когда она будет принимать входные вызовы.

Таким образом, вы, вероятно, захотите заменить вашу последовательность

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

на цикл, который вызывает Put один раз вкаждая итерация с оператором select, который выполняется каждый раз.

Вот демонстрационная программа, которую я только что создал.Счетчик увеличивается один раз в секунду (приблизительно).Значение счетчика печатается, если Running истинно;в противном случае цикл продолжается молча.Основная программа альтернативно вызывает Pause и Resume каждые 3 секунды.

Предложение 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;

Там может быть более элегантный подход, чем этот.Прошло много времени с тех пор, как я действительно использовал Аду.

4 голосов
/ 17 ноября 2011

Одним из аспектов замысла, лежащего в основе языка Ada, является явное поведение. Выполнение задачи, которая внезапно оказалась «замороженной» в произвольной точке по непонятной причине - в задаче не было ничего, что могло бы привести к такому поведению - в лучшем случае приводило бы в замешательство. И, возможно, ошибка, если задача не предназначена для непредвиденного, произвольного приостановления выполнения. (Я знаю, что временные ограничения ОС навязывают это поведение при выполнении программ, но это функция ОС, а не языка программирования.)

Короче говоря, я бы серьезно усомнился в подходе к дизайну, который основывался на внешнем приостановлении задачи. Задача должна знать, в каком состоянии она находится, и, таким образом, знать, когда она может быть приостановлена, и какие инварианты нужно держать для приостановки, чтобы обеспечить правильное возобновление.

4 голосов
/ 17 ноября 2011

Ух ты, давно не видел вопроса об Аде. В любом случае, когда вам нужно приостановить задачу, вы используете зарезервированные слова 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 голосов
/ 17 ноября 2011

Чтобы получить гранулярность, вам нужно правильно прервать вашу последовательность put("x"), вы должны написать это как процедуру, которая сохраняет свое состояние в пределах task my_task (зная, какая атомарная инструкция была выполнена).

Тогда я думаю, что вы можете использовать guard на вашем State_Type и выполнять только тогда, когда не заморожены. конечно, это не остановит задачу, она просто защитит последовательность put("x") от выполнения (что, я думаю, вам и нужно!)

1 голос
/ 19 ноября 2011

В ответ на один из комментариев вы упомянули, что вы хотите этого из-за некоторых общих ресурсов.Тогда кажется, что ответом является то, что вы хотите обернуть этот общий ресурс в защищенный объект и вызвать процедуры / записи этого защищенного объекта из задачи;пока задача ставится в очередь (или выполняет) вызов защищенного объекта, она переводится в состояние «заблокировано» до тех пор, пока она не вернется, тем самым «замораживая» дальнейшую обработку.

Редактировать: добавлен пример кода.


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] потенциально блокирует ... поэтому, хотя вышеприведенное никогда не должно тупиковать, такое фактически не гарантируется.

1 голос
/ 17 ноября 2011

Если у вас есть конкретная строка, в которой вы всегда хотели бы, чтобы ваша задача ожидала решения, решение довольно простое: просто поместите туда оператор accept. Задача будет зависать до тех пор, пока какая-либо другая задача не вызовет эту запись.

Если вы хотите обобщить эту идею (чтобы можно было выполнить несколько задач в ожидании, или несколько разных задач могут безопасно выполнить действие освобождения), может быть лучше инкапсулировать вашу переменную State внутри защищенного объекта (как NWS). предложил). Функция защищенных объектов была введена в язык специально для облегчения создания объектов синхронизации для таких задач.

0 голосов
/ 22 ноября 2011

Я также получил ответ от Anh Vo в 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;

Признаюсь, я думаю, что это решение очень элегантно.

...