【问题标题】:Questions about implementation of worker tasks controlled by single master task关于单主任务控制的worker任务实现的问题
【发布时间】:2019-10-23 05:11:32
【问题描述】:

我想实现一个主任务来控制一个工作任务的多个实例。每个工作任务都有三个不同的阶段:

  1. 初始化
  2. 工作
  3. 报告结果

一开始,主任务应该初始化所有工作任务(同时)。然后每个工作任务有s 秒才能成功完成其初始化,但不能保证在s 秒内完成。

我必须让主任务监控所有工作任务的初始化状态有哪些有效的可能性(信号机制)?我想让每个工作任务访问一个工作任务特定的受保护类型对象,并设置一个布尔标志,该标志将由各个工作任务成功完成初始化后设置。

在主任务触发所有工作任务的初始化后,它可以记住当前时间并进入循环以使用在受保护对象类型中声明的函数来定期轮询工作任务初始化状态以检索初始化状态。如果所有工作任务都已初始化或经过s 秒,则循环退出。

我是否必须使用适当的时间值在监视器循环内使用延迟语句来使用这种轮询概念?我阅读了有关入口调用超时的信息。我可以使用这样的超时来阻止轮询吗?

在工作任务成功完成初始化后,它应该等待来自控制任务的信号以执行一个工作包。所以我认为一个工作任务应该有一个Do_Work 条目,因此主任务应该在一个循环中为所有工作任务调用这些条目,对吗?

主任务可以使用适当的机制来检查所有工作任务是否已完成其工作包。发生这种情况后,工作任务应该以确定性的方式(不是同时)报告他们的工作结果。因此,如果我在工作任务中使用Report_Result 条目来等待来自主任务的信号,则在控制任务的循环中调用此条目将导致报告结果的不确定顺序。是否也可以以阻塞方式调用这些条目(如普通过程调用)?

【问题讨论】:

  • 也许我误解了这个问题,但你能详细说明一下初始化时间限制吗?时间预算s [s] 是硬性要求还是仅仅是备注?假设您有 10 个任务。其中 8 个能够在 s [s] 内初始化,其中 2 个不能。那么预期和/或可接受的行为是什么?只是继续将工作委派给及时初始化的 8 个任务,并丢弃没有初始化的 2 个?必须取消这两个任务的初始化吗?还是等待所有worker都初始化好?
  • 抱歉,我的问题描述不够完整/清晰。如果工作人员未能在给定时间内初始化,则应将其丢弃。
  • 我不明白你为什么想要一个主任务来控制工作任务。与许多语言不同,Ada 任务是自调度的,通常不需要控制。任务池的典型实现是在 PragmARC.Job_Pools (github.com/jrcarter/PragmARC/blob/Ada-07/pragmarc-job_pools.ads) 中,没有主人控制它们。
  • 我有兴趣在所有工作任务完成当前处理周期中分配的工作后获取每个工作任务的结果。结果应该以确定的顺序发送到另一个系统。发送完所有结果后,将启动一个新的处理周期。如果不使用某种控制实例,我不知道如何实现这一点。

标签: multithreading ada


【解决方案1】:

您是正确的,主任务可以为每个工作任务调用 Do_Work 条目。 同样,主任务可以调用所有工作任务的Report_Result条目。

实现此目的的一种简单方法是为工作任务创建一个任务类型,然后创建一个工作任务数组。

    procedure Master is
       task type Workers is
          entry Do_Work;
          entry Report_Result;
       end Workers;

       Team : array(1..5) of Workers;

    begin
       -- Initialization will occur automatically
       -- Signal workers to Do_Work

       for Worker of Team loop
          Worker.Do_Work;
       end loop;

       -- Create a loop to signal all reports
       -- While the workers may finish in a random order, the 
       -- reporting will occur in the order of the array indices

       for Worker of Team loop
          Worker.Report_Result;
       end loop;
    end Master;

此示例不完整,因为它没有为 Workers 任务类型定义任务主体。该计划的重要特点是:

  • 当执行到达 Master 中的 begin 语句时,Team 数组中工作人员的任务初始化开始。
  • Master 将等待 Team 的每个元素接受对 Do_Work 的入口调用。
  • Team 的每个元素都将在接受语句处等待 Master 调用 Do_Work 条目。
  • master 将等待 Team 的每个元素接受 Report_Result 条目。
  • Team 的每个元素都将在其接受 Report_Result 时等待 master 调用该条目。

Ada Rendezvous 机制巧妙地协调 master 和每个 worker 之间的所有通信。

【讨论】:

  • 感谢您的详细回答。 ;) Report_Result 条目将以确定的顺序被调用,但例如,如果索引为 1 的工作人员需要 10 秒来创建和发送报告,而索引为 2 的工作人员只需 5 秒即可执行相同操作,则外部接收器会以错误的顺序接收消息。
  • 另一种方法是将报告的创建作为 Do_Work 条目控制的最后一个操作。然后 Report_Result 可以被限制为报告的实际发送,然后以正确的顺序发生。
  • 我认为这取决于通信系统。如果第一个报告大于第二个报告,则第二个报告的发送过程可能更快。如果Report_Result 的行为类似于阻塞过程调用,并且通信系统的Send 过程在消息发送之前也会阻塞,那么行为将是确定性的(我的想法)。
  • 如果任务以accept Report_Result do 结束,报告结果end accept; 那么调用者确实会阻塞,直到发送结果。
【解决方案2】:

如果您真的希望工作人员向经理发出他们已完成的信号,您可以做的一件事是将经理的访问权限传递给工作人员并提供一个条目供他们调用。当该信号发生时,您必须决定经理和员工如何互动。

举个例子,我有一个经理保存了一个工人数组和两个对这些工人的访问列表(因为它们是有限的类型,你必须使用访问变量)。一个列表将跟踪所有可用的工人,另一个将跟踪当前正在做某事的工人。当工人完成他们的工作时,他们会向将他们从忙碌列表中删除并将他们放入可用列表的经理发出信号。当客户端请求经理做更多工作时,它会从可用列表中拉出一个工作人员并将其放在忙碌列表中并启动工作人员。这是在 GNAT 7.1.1 中编译的示例:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Bounded_Doubly_Linked_Lists;

procedure Hello is

    package Tasks is

        type Worker;
        type Worker_Access is access all Worker;

        package Lists is new Ada.Containers.Bounded_Doubly_Linked_Lists
            (Element_Type => Worker_Access);

        task type Manager is

            -- Called by client code
            entry Add_Work;
            entry Stop;

            -- Only called by workers to signal they are
            -- finished
            entry Signal(The_Position : in out Lists.Cursor);
        end Manager;

        task type Worker(Boss : not null access Manager) is
            entry Start(The_Position : Lists.Cursor);
        end Worker;

    end Tasks;

    package body Tasks is

        task body Worker is
            Position : Lists.Cursor := Lists.No_Element;
        begin
            loop
                select
                    accept Start(The_Position : Lists.Cursor) do
                        Position := The_Position;
                    end Start;

                    -- Do stuff HERE
                    delay 0.005;

                    -- Finished so signal the manager
                    Boss.Signal(Position);
                    Position := Lists.No_Element;

                or
                    terminate;
                end select;
            end loop;
        end Worker;

        Worker_Count : constant := 10;

        task body Manager is

            -- Worker Pool
            Workers : array(1..Worker_Count) 
                of aliased Worker(Manager'Unchecked_Access);  -- ' Fixing formatting

            -- Use 2 lists to keep track of who can work and who
            -- is already tasked
            Bored : Lists.List(Worker_Count);
            Busy  : Lists.List(Worker_Count);

            -- Gonna call a couple of times, so use a nested
            -- procedure.  This procedure removes a worker 
            -- from the Busy list and places it on the Bored
            -- list.
            procedure Handle_Signal(Position : in out Lists.Cursor) is
            begin
                Put_Line("Worker Completed Work");
                Bored.Append(Lists.Element(Position));
                Busy.Delete(Position);
            end Handle_Signal;

            use type Ada.Containers.Count_Type;

        begin

            -- Start off all workers as Bored
            for W of Workers loop
                Bored.Append(W'Unchecked_Access);  -- ' Fixing formatting
            end loop;

            -- Start working
            loop
                select
                    when Bored.Length > 0 =>
                    accept Add_Work do
                        -- Take a worker from the Bored list, put it
                        -- on the busy list, and send it off to work.
                        -- It will signal when it is finished
                        Put_Line("Starting Worker");
                        Busy.Append(Bored.First_Element);
                        Bored.Delete_First;
                        Busy.Last_Element.Start(Busy.Last);
                    end Add_Work;
                or
                    accept Stop;
                    Put_Line("Received Stop Signal");

                    -- Wait for all workers to finish
                    while Busy.Length > 0 loop
                        accept Signal(The_Position : in out Lists.Cursor) do
                            Handle_Signal(The_Position);
                        end Signal;
                    end loop;

                    -- Break out of loop
                    exit;
                or
                    accept Signal(The_Position: in out Lists.Cursor) do
                        Handle_Signal(The_Position);
                    end Signal;
                end select;
            end loop;

            -- Work finished!
            Put_Line("Manager is Finished");
        end Manager;

    end Tasks;

    Manager : Tasks.Manager;

begin

    for Count in 1 .. 20 loop
        Manager.Add_Work;
    end loop;

    Manager.Stop;

    -- Wait for task to finish
    loop
        exit when Manager'Terminated;
    end loop;

    Put_Line("Program is Done");

end Hello;

我使用光标帮助员工记住他们在忙碌列表中的位置,以便他们可以告诉经理,并且可以快速移动。

样本输出:

$gnatmake -o hello *.adb
gcc -c hello.adb
gnatbind -x hello.ali
gnatlink hello.ali -o hello
$hello
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Worker Completed Work
Worker Completed Work
Starting Worker
Worker Completed Work
Starting Worker
Starting Worker
Received Stop Signal
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Worker Completed Work
Manager is Finished
Program is Done

请注意,您可以美化它并隐藏一堆东西,我只是想快速举个例子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 2017-08-07
    • 2018-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多