Ada:像 pthread_barrier_wait 这样的行为?
Ada: behavior like pthread_barrier_wait?
我正在尝试在 Ada 中实现一个屏障,它与 C 的 pthread_barrier_wait 具有相似的功能。 Ada 2012 有 Ada.Synchronous_Barriers 但在我的系统上不可用(debian lenny 上的 gnu-gnat)。
更具体地说,如何让所有等待任务同时从障碍中释放,理想情况下,让其中一个任务做一些特殊的事情,而不使用 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
也许条目的 'count 属性会有用 - 这是您正在寻找的东西吗?使用任务 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
这扩展了 Leon 的答案以实现所需的功能。它使用单个屏障对象并标记一个任意任务来做一些特殊的事情。
编辑:结合 Jacob 的见解进一步简化障碍并实现最初的目标
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
我正在尝试在 Ada 中实现一个屏障,它与 C 的 pthread_barrier_wait 具有相似的功能。 Ada 2012 有 Ada.Synchronous_Barriers 但在我的系统上不可用(debian lenny 上的 gnu-gnat)。
更具体地说,如何让所有等待任务同时从障碍中释放,理想情况下,让其中一个任务做一些特殊的事情,而不使用 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
也许条目的 'count 属性会有用 - 这是您正在寻找的东西吗?使用任务 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
这扩展了 Leon 的答案以实现所需的功能。它使用单个屏障对象并标记一个任意任务来做一些特殊的事情。
编辑:结合 Jacob 的见解进一步简化障碍并实现最初的目标
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