有关由单个主任务控制的工作者任务的实现的问题

时间:2019-06-07 17:09:32

标签: multithreading ada

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

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

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

我必须让主任务监视所有辅助任务的初始化状态的有效方式(信号机制)吗?我想让每个工作任务都可以访问一个特定于工作任务的受保护类型对象,该过程具有设置布尔标志的过程,该标志将由各个工作任务成功完成初始化后设置。

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

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

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

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

2 个答案:

答案 0 :(得分:4)

您正确的是,主任务可以为每个辅助任务调用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数组中的worker进行任务初始化。
  • Master将等待Team的每个元素接受对Do_Work的进入呼叫。
  • Team的每个元素都将在接受声明中等待Master调用Do_Work条目。
  • 主管将等待团队的每个要素接受Report_Result条目。
  • Team的每个元素都将等待其接受Report_Result,以便母版调用该条目。

Ada交会机制巧妙地协调了主人与每个工人之间的所有通信。

答案 1 :(得分:3)

如果您真的希望工作人员向经理发出信号,告知他们他们已经完成了,您可以做的一件事就是将管理人员的访问权限传递给工作人员,并提供一个供他们呼叫的条目。您必须确定在发生信号时经理和工人之间如何交互。

作为一个示例,我有一个管理器保留一个Workers数组和两个对这些worker的访问权限列表(由于它们是有限的类型,因此您必须使用访问变量)。一个列表将跟踪所有可用的工人,而另一个列表将跟踪当前正在做某事的工人。当工人完成工作时,他们会向经理发出信号,要求经理将他们从忙碌清单中删除并将其放入可用清单中。当客户要求经理做更多工作时,它会从可用列表中拉出一个工作人员,并将其放在繁忙列表中,然后开始工作。这是在GNAT 7.1.1中编译的示例:

 (?<! \S )
 \$?
 (                             # (1 start)
      3 \d{2} ,? \d{3} 
   |  [4-9] \d{2} ,? \d{3} 
   |  1 0{6}
   |  1 ,? 000 ,? 000
 )                             # (1 end)
 (                             # (2 start)
      \.
      \d{0,2} 
 )?                            # (2 end)
 (?! \S )

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

示例输出:

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;

请注意,您可以修饰一下并隐藏一堆东西,我只是想快速举例一下。