Ada 中的多线程

Multithreading in Ada

我正在学习 Ada,但在理解并发模型方面遇到了一些问题。下面的测试应用程序将创建 3 个并行的 运行 任务,并简单地打印一系列数字。如果我使用没有 entry 的任务,那么一切都很好,但如果我使用条目,过程调用块并且根本不会发生并发。

我知道有可能实现互斥和同步执行,但我无法理解如何分离任务,甚至可能创建多个任务。

q_multithreading.ads:

package Q_MULTITHREADING is

  task type TASK_LOOP is
  end TASK_LOOP;

  type TASK_LOOP_ACCESS is access TASK_LOOP;

  --===========================================================================

  task type TASK_ENTRY_LOOP is
    entry P_ITERATE(to : in Integer);
  end TASK_ENTRY_LOOP;

  type TASK_ENTRY_LOOP_ACCESS is access TASK_ENTRY_LOOP;

  --===========================================================================

  procedure P_EXECUTE_NO_ENTRY;

  procedure P_EXECUTE_ENTRY(to : in Integer);

end Q_MULTITHREADING;

q_multithreading.adb:

with Ada.Text_IO;

package body Q_MULTITHREADING is

  V_ID_COUNTER : Integer := 1;

  --===========================================================================

  task body TASK_LOOP is

    V_ID : Integer := -1;

  begin

    V_ID := V_ID_COUNTER;
    V_ID_COUNTER := V_ID_COUNTER + 1;

    for i in 1 .. 15 loop
      delay 0.1;
      Ada.Text_IO.Put_Line("[" & Integer'Image(V_ID) & "] " &
                             Integer'Image(i));
    end loop;

    V_ID_COUNTER := V_ID_COUNTER - 1;

  end TASK_LOOP;

  --===========================================================================

  task body TASK_ENTRY_LOOP is

    V_ID : Integer := -1;

  begin

    V_ID := V_ID_COUNTER;
    V_ID_COUNTER := V_ID_COUNTER + 1;

    accept P_ITERATE(to : in Integer) do
      for i in 1 .. to loop
        delay 0.1;
        Ada.Text_IO.Put_Line("[" & Integer'Image(V_ID) & "] " &
                               Integer'Image(i));
      end loop;
    end P_ITERATE;

    V_ID_COUNTER := V_ID_COUNTER - 1;

  end TASK_ENTRY_LOOP;

  --===========================================================================

  procedure P_EXECUTE_NO_ENTRY is

    V_TASK1, V_TASK2, V_TASK3 : TASK_LOOP_ACCESS;

  begin

    V_ID_COUNTER := 1;

    Ada.Text_IO.Put_Line("Starting task 1 ...");
    V_TASK1 := new TASK_LOOP;

    Ada.Text_IO.Put_Line("Starting task 2 ...");
    V_TASK2 := new TASK_LOOP;

    Ada.Text_IO.Put_Line("Starting task 3 ...");
    V_TASK3 := new TASK_LOOP;

  end P_EXECUTE_NO_ENTRY;

  --===========================================================================

  procedure P_EXECUTE_ENTRY(to : in Integer) is

    V_TASK1, V_TASK2, V_TASK3 : TASK_ENTRY_LOOP_ACCESS;

  begin

    V_ID_COUNTER := 1;

    V_TASK1 := new TASK_ENTRY_LOOP;
    Ada.Text_IO.Put_Line("Starting task 1 ...");
    V_TASK1.P_ITERATE(to); -- blocking

    V_TASK2 := new TASK_ENTRY_LOOP;
    Ada.Text_IO.Put_Line("Starting task 2 ...");
    V_TASK2.P_ITERATE(to - 5); -- blocking

    V_TASK3 := new TASK_ENTRY_LOOP;
    Ada.Text_IO.Put_Line("Starting task 3 ...");
    V_TASK3.P_ITERATE(to + 3); -- blocking

  end P_EXECUTE_ENTRY;

end Q_MULTITHREADING;

正如我已经提到的,如果我调用 P_EXECUTE_NO_ENTRY,输出将是无序的并且任务与主线程分离。另一方面 *P_EXECUTE_ENTRY(to : in Integer) 导致阻塞过程调用,输出就像一个不使用任务的应用程序。

在 Ada 中如何并发执行具有条目的任务?

此外,我是否也必须取消分配任务? (网上的例子没做)

任务在集合点期间同步(即只要您在 accept 语句内)。

通常,您会将 accept 语句限制为复制传递的参数 to/from 包含 accept 语句的任务:

accept Start (Steps : in Positive) do
   Count := Steps;
end Start;

for I in 1 .. Count loop
   ...

当你说

accept P_ITERATE(to : in Integer) do
  for i in 1 .. to loop
    delay 0.1;
    Ada.Text_IO.Put_Line("[" & Integer'Image(V_ID) & "] " &
                           Integer'Image(i));
  end loop;
end P_ITERATE;

调用方在 end P_ITERATE 之前一直处于阻塞状态,因此整个循环在 P_EXECUTE_ENTRY 可以继续执行下一个任务之前完成。

要解决此问题,请将循环计数保存在任务变量中并在 accept:

之外执行循环
accept P_ITERATE(to : in Integer) do
  count := to;
end P_ITERATE;
for i in 1 .. count loop
  delay 0.1;
  Ada.Text_IO.Put_Line("[" & Integer'Image(V_ID) & "] " &
                         Integer'Image(i));
end loop;

至于取消分配任务 - 大多数终止的程序都不会打扰,因为操作系统会在进程退出时取消分配内存。在这种情况下,您可能不会在任务实际终止之前抽空解除分配;取消分配 运行 任务可能会导致意外行为。我认为如何管理这应该是一个不同的问题。