2015-12-10 2 views
4

Я пытаюсь реализовать барьер в Ada, который имеет аналогичную функциональность для pthread_barrier_wait C. Ada 2012 имеет Ada.Synchronous_Barriers, но это недоступно в моей системе (gnu-gnat на debian lenny).Ada: поведение, подобное pthread_barrier_wait?

В частности, как я могу получить все задачи ожидания, которые будут выпущены из барьера в то же время, и, в идеале, одна из этих задач сделает что-то особенное, не используя Ada 2012? Ниже приведена очень малооптимальная реализация. Что может быть лучшим подходом?

with Ada.Text_IO; use Ada.Text_IO; 
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; 

procedure foobar is 
    protected Synchronizer is 
     entry Ready_For_Action; -- prepares for tasks to wait at barrier 
     entry Wait_For_Release; -- barrier 
     -- do work here 
     entry Done;    -- signals that all tasks are done 
     entry Wait_For_Others; -- prepares for prepare to wait at barrier 
    private 
     ready, active: Natural := 0; 
     -- two state variables seem to be needed as entry conditions can't 
     -- safely modify the condition variable as that influences wait 
     -- state in other tasks 
    end Synchronizer; 

    NUM_OBJECTS: constant := 3; 

    protected body Synchronizer is 
     entry Ready_For_Action when active = 0 is 
     begin 
     ready := ready + 1; 
     end Ready_For_Action; 
     -- 
     entry Wait_For_Release when ready = NUM_OBJECTS is 
     begin 
     active := active + 1; 
     end Wait_For_Release; 
     -- 
     entry Done when active = NUM_OBJECTS is 
     begin 
     ready := ready - 1; 
     end Done; 
     -- 
     entry Wait_For_Others when ready = 0 is 
     begin 
     active := active - 1; 
     end wait_for_others; 
     -- 
    end Synchronizer; 

    task type Foo(N: Natural); 

    task body Foo is 
     id: Natural := N; 
    begin 
     for iter in 1..3 loop 
     Synchronizer.Ready_For_Action; 
     Synchronizer.Wait_For_Release; 
     -- task N doing something special 
     if id = 1 then new_line; end if; 
     -- do stuff here 
     delay 0.1; 
     put(id); new_line; 
     -- re-sync 
     Synchronizer.Done; 
     Synchronizer.Wait_For_Others; 
     end loop; 
    end Foo; 
    Task1: Foo(1); 
    Task2: Foo(2); 
    Task3: Foo(3); 
begin 
    Null; 
end foobar; 

выход программы:

$ ./foobar 
    3 
    1 
    2 

    3 
    1 
    2 

    3 
    2 
    1 
+4

Этот пакет Ada2012 появился в GCC 4.7. Это, безусловно, присутствует в GNAT GPL 2015 - я просто загрузил исходный пакет и использовал его (возможно, лучше всего переименовать его в «Ada_Synchronous_Barriers», чтобы остановить путаницу компилятора). Вы можете сказать, какую версию GNAT у вас есть в вашей системе, например, gnatls -v'. –

+0

@SimonWrite Отличное решение для pracitcal. gnatls показывает 4.6 на моей машине – aquilonis

ответ

2

Может быть «атрибут подсчета на записи будет полезно - это та вещь, которую вы ищете? Использование идентификаторов задач для того, чтобы сделать что-то другое, кажется разумным (или если оно достаточно различно, вы можете просто создать новый тип задачи).

No_Of_Tasks : Natural := 3; 
    -- 
protected Barrier is 
    entry Continue; 
private 
    Released : Boolean := False; 
end Barrier 
    -- 
protected body Barrier is 
    entry Continue when (Released or else Continue'count = No_Of_Tasks) 
     Released := Continue'count > 0; -- the last task locks the barrier again 
    end Continue      
end Barrier       
+0

+1 Атрибут count - это ключ - спасибо. Я расширил ваш подход ниже, используя один барьер с двумя записями, которые работают в цикле. – aquilonis

+1

. Вы можете взглянуть на раздел 4.7.2 в разделе «Создание параллельных, встроенных и приложений реального времени с помощью Ada». Из-за правил приоритета для защищенных объектов вам не нужны две записи. –

1

Это удлиняет ответ Леона для достижения желаемой функциональности. Он использует один объект-барьер и помещает одну произвольную задачу, чтобы сделать что-то особенное.

EDIT: понимание Incorporated Якоба еще больше упростить барьер и достичь первоначальной цели

with Ada.Text_IO; use Ada.Text_IO; 
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; 

procedure bar2 is 
    NUM_TASKS: constant := 3; 

    protected Barrier is 
     entry Wait_For_Release(the_chosen_one: out Boolean); 
    private 
     released: Boolean := False; 
    end Barrier; 

    protected body Barrier is 
     entry Wait_For_Release(the_chosen_one: out Boolean) 
     when (Released or else Wait_For_Release'count = NUM_TASKS) is 
     begin 
     the_chosen_one := False; 
     if Wait_For_Release'count = NUM_TASKS-1 then 
      the_chosen_one := True; 
      released := True; 
     elsif Wait_For_Release'count = 0 then 
      released := False; 
     end if; 
     end Wait_For_Release; 
    end Barrier; 

    task type Foo(N: Natural); 
    task body Foo is 
     id: Natural := N; 
     the_chosen_one: Boolean; 
    begin 
     for iter in 1..5 loop 
     Barrier.Wait_For_Release(the_chosen_one); 
     if the_chosen_one then 
      new_line; 
     end if; 
     put(id);  -- do stuff here 
     end loop; 
    end Foo; 

    Task1: Foo(1); 
    Task2: Foo(2); 
    Task3: Foo(3); 
begin 
    Null; 
end bar2; 

Пример вывода:

$ ./bar 

      1   2   3 
      3   1   2 
      1   2   3 
      1   3   2 
      3   2   1 
+2

Это излишне сложно, так как правила приоритета для защищенных объектов означают, что всем задачам ожидания разрешено запускать запись, прежде чем какие-либо новые получат доступ к очереди. –

+0

О да! Вначале я полностью забыл о внутреннем прогрессе, когда написал свой ответ. – Leon

Смежные вопросы