2015-12-10 28 views
4

我想實現的Ada的障壁的具有類似的功能到C的pthread_barrier_wait。 Ada 2012有Ada.Synchronous_Barriers,但這在我的系統上不可用(debian lenny上的gnu-gnat)。Ada:像pthread_barrier_wait這樣的行爲?

更具體地說,我怎麼能得到所有等待任務從一個障礙的〜同一時間被釋放,理想的是有這些任務之一做些特別的事情,而無需使用艾達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',雖然,停止編譯器感到困惑)。順便說一句,你可以通過'gnatls -v'告訴你係統上的GNAT版本。 –

+0

@SimonWrite偉大的實踐解決方案。 gnatls顯示4.6我的機器 – aquilonis

回答

2

也許在項「計數屬性將是有益的 - 這是那種你要找的東西嗎?使用任務ID讓一個人做一些不同的事情似乎是合理的(或者如果它足夠不同,你可以創建一個新的任務類型)。

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「計數屬性上是關鍵 - 謝謝。我下面伸出你的方法,使用單一的屏障,在一個循環 – aquilonis

+1

你可能想看看在「建設並行,嵌入式和Ada語言實時應用程序」部分4.7.2工程兩個條目。由於受保護對象的優先權規則,您不需要兩個條目。 –

1

這擴展了Leon的答案,以實現所需的功能。它使用一個屏障對象並標記一個任意的任務來做一些特殊的事情。

編輯:引入的雅各的洞察力,以進一步簡化屏障,實現最初的目標

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