ADA 95教程 高级特性 额外的任务主题

在前三章中,我们已经讨论了关于任务处理的大部分主题,但是为了让你充分利用任务处理,我们还必须考虑更多的任务处理主题。本章的主题绝对是高级任务主题,您可能希望忽略这些材料,直到您在使用Ada方面获得了相当多的经验。您应该花时间至少通读一下这篇材料,对其进行一些了解,并认识到Ada编程语言中存在这些功能。

 

任务类型

Example program ------> e_c29_p1.ada

                                     -- Chapter 29 - Program 1
with Ada.Text_IO;
use Ada.Text_IO;

procedure TaskType is

task type SHORT_LINE is
end SHORT_LINE;

task type LONG_LINE is
end LONG_LINE;

Cow, Dog, Pig          : SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;

task body SHORT_LINE is
begin
   for Index in 1..4 loop
      delay 0.0;
      Put_Line("This is a short line");
   end loop;
end SHORT_LINE;

task body LONG_LINE is
begin
   for Index in 1..3 loop
      delay 0.0;
      Put_Line("This is a much longer line to be displayed");
   end loop;
end LONG_LINE;

begin
   Put_Line("This is an example of use of a task type");
end TaskType;




-- Result of execution

-- This is an example of use of a task type
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line

检查名为e_c29_p1的程序。ada中的任务类型示例。这看起来是一个非常奇怪的结构,但是如果您花时间思考它,您会发现它在Ada程序中有一个位置,而且它可能非常有用。

在之前所有带有任务的程序中,我们都声明了一个任务规范,然后是一个任务主体,但这并不是一般情况。它实际上是声明任务的一种速记符号,有点类似于匿名类型。任务定义的更一般情况是声明一个任务类型规范,然后是任务主体,然后是具有声明类型的任务。事实上,该任务的声明方式与我们在Ada中声明变量的方式基本相同,即任务名称后跟冒号和任务类型。在本程序中,我们在第7和8行声明了第一个任务类型规范,在第16到22行声明了相应的任务主体。任务本身在第13行中声明,其中Cow被声明为任务类型SHORT_LINE。实际上,这一行声明了三个任务,另外两个分别命名为Dog和Pig。

 

这到底是什么意思?

如果我们声明了一个数据类型,然后使用该数据类型声明三个变量,那么理解发生了什么就不会有问题,因为在本教程中您一直在这样做。对您来说,这可能有点难以理解,但我们对该任务做的是相同的事情,只是我们声明了一段代码的类型,然后生成并执行该代码的三个副本。它是一样的如果我们宣布任务规范命名牛一样,鉴于在7和8行,然后身体一个任务叫牛一样,鉴于在16到22日行和其他两个任务也做了相同的事情叫狗和猪。我们不是将代码复制三次,而是为任务声明一个模式,然后让系统运行它的三份副本。

该任务本身非常简单,由一个零延迟的循环和一个向标准输出设备(监视器)输出消息的语句组成。任务本身是在任务体声明之前在第13行声明的,但这应该不会对您造成任何困难,因为我们之前已经使用了子程序和类型的部分声明。

这里应该提到一点。任务类型总是有限的私有类型,这意味着您不能对它进行任何操作。对于有限的私有类型,赋值和比较是不可用的,因此防止任务执行这些操作是有意义的。

 

声明了两种任务类型

除了名为SHORT_LINE的任务类型外,还有一个名为LONG_LINE的任务类型与第一个类似,它只输出三行直到完成,但输出的文本行要长得多。声明并执行该类型的两个副本。

所有这一切的最终结果是生成并执行五个并行运行的任务,除了我们在前面章节讨论过的零时间延迟语句外,它们之间没有交会。

 

他们什么时候跑?

我们在前面的章节中讨论了每个任务是如何开始运行的,这没有什么不同。当所有五个任务都已完全细化,并且都在各自的begin语句处等待时,当主程序到达其begin语句处时,所有的任务都开始执行。请记住,有6个任务将主程序算作一个任务。同样地,当所有的任务,包括主程序,到达它们的终点时,一个有序的终止生效,程序停止执行,将控制权交还给操作系统。

编译并运行这个程序,您将看到所有五个任务确实都像上面描述的那样运行。在第13和14行中添加一些额外的任务,看看任务类型完成后添加额外任务有多容易。这将是非常值得你花时间学习这个程序,直到你理解这个材料,因为它将在接下来的几个示例程序中使用。

 

作为记录一部分的任务

这里将不进行说明,但是可以将任务类型变量声明为记录的一部分。这样的记录可用于声明任务变量和一些其他变量,供任务使用。

 

任务的数组

Example program ------> e_c29_p2.ada

                                     -- Chapter 29 - Program 2
with Ada.Text_IO;
use Ada.Text_IO;

procedure TaskArry is

task type SHORT_LINE is
end SHORT_LINE;

task type LONG_LINE is
end LONG_LINE;

Cow                    : array (1..4) of SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;

task body SHORT_LINE is
begin
   for Index in 1..4 loop
      delay 0.0;
      Put_Line("This is a short line");
   end loop;
end SHORT_LINE;

task body LONG_LINE is
begin
   for Index in 1..3 loop
      delay 0.0;
      Put_Line("This is a much longer line to be displayed");
   end loop;
end LONG_LINE;

begin
   Put_Line("This is an example of use of a task type array");
end TaskArry;




-- Result of execution

-- This is an example of use of a task type
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line

 

检查名为e_c29_p2的程序。Ada作为任务类型数组的一个例子。除了第13行添加了一个任务数组外,这个程序与上一个程序完全相同。任务名称Cow被声明为一个名为Cow(1), Cow(2),…的任务数组。Cow(4),所以当执行开始时,有4个这种类型的任务并发运行。这四个是在前面程序中类型为LONG_LINE的两个之外的。关于这个程序,不需要多说什么,只要编译并执行它,就可以看到所有6个任务确实与主程序中的任务并行运行。实际上有7个任务并行运行。将Cow数组增加到6个任务,并看到9个任务在运行,其中6个在数组中,2个是另一种类型,还有主程序。

 

任务访问类型

Example program ------> e_c29_p3.ada

                                     -- Chapter 29 - Program 3
with Ada.Text_IO;
use Ada.Text_IO;

procedure TaskAces is

task type SHORT_LINE is
end SHORT_LINE;

task type LONG_LINE is
end LONG_LINE;

type LONG_POINT is access LONG_LINE;

Cow, Dog, Pig          : SHORT_LINE;
Elephant, Hippopotamus : LONG_POINT;

task body SHORT_LINE is
begin
   for Index in 1..4 loop
      delay 0.0;
      Put_Line("This is a short line");
   end loop;
end SHORT_LINE;

task body LONG_LINE is
begin
   for Index in 1..3 loop
      delay 0.0;
      Put_Line("This is a much longer line to be displayed");
   end loop;
end LONG_LINE;

begin

   Put_Line("This is an example of use of a task type");
   Elephant := new LONG_LINE;
   Hippopotamus := new LONG_LINE;

end TaskAces;




-- Result of execution

-- This is an example of use of a task type
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed

检查名为e_c29_p3的程序。Ada是一个访问类型的例子,可以用来访问一个任务。一旦我们能够通过访问变量访问任务,我们还可以动态地分配任务并执行它。我们将在本示例程序中演示这个操作。

这个程序与前两个程序几乎完全相同,除了名为Elephant和Hippopotamus的变量不是任务类型变量,而是任务访问类型变量,因为我们在第13行中声明了类型LONG_POINT作为访问变量。

当执行此程序时,当主程序执行时,三个静态声明的任务开始执行,但两个访问类型变量不执行任何操作,因为它们只是尚未访问任何内容的访问类型。主程序向监视器输出一行文本,然后在第37行中动态分配任务类型LONG_LINE的副本,然后新任务开始执行。在第38行中分配了另一个任务并开始执行。所有五个任务将运行到完成,并有一个有序的终止。

在这种情况下,主程序是两个动态分配任务的父任务,它们被称为主程序的子任务。

 

什么时候开始执行?

这个程序和前两个程序的最大区别在于,分别的任务何时开始执行。所有静态声明的任务在主程序开始时开始执行,而动态分配的任务在被分配时开始执行。通过研究这三个程序的执行结果可以清楚地看到这一点。在本程序中,两个已分配任务中的每个任务的第一个输出都滞后于静态声明任务的第一个输出。

请确保编译并执行此程序,并将您的编译器的输出与作者的Ada编译器生成的输出进行比较。添加一些访问变量并分配额外任务只是为了获得经验,这对您来说是一件很简单的事情。

 

这是一个非常低效的任务分配例子

Example program ------> e_c29_p4.ada

                                    -- Chapter 29 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Parallel is

DAYS : constant := 7;
EMPLOYEES : constant := 11;

type WORKED_ARRAY is array (1..EMPLOYEES, 1..DAYS) of INTEGER;
type TOTAL_ARRAY is array (1..EMPLOYEES) of INTEGER;

Hours_Worked  : WORKED_ARRAY;
Weekly_Totals : TOTAL_ARRAY;
Result        : INTEGER;

task type SUMMING_TASK_TYPE is
   entry Start_Sums(E_No : in INTEGER);
   entry Return_Sum(Result : out INTEGER);
end SUMMING_TASK_TYPE;

Adding_Task : array(1..EMPLOYEES) of SUMMING_TASK_TYPE;

task body SUMMING_TASK_TYPE is
Total : INTEGER := 0;
Local_E_No : INTEGER;
begin
   accept Start_Sums(E_No : in INTEGER) do
      Local_E_No := E_No;
   end Start_Sums;

   for Index in 1..Days loop
      Total := Total + Hours_Worked(Local_E_No, Index);
   end loop;

   accept Return_Sum(Result : out INTEGER) do
      Result := Total;
   end Return_Sum;
end SUMMING_TASK_TYPE;

begin
   for Emp_Number in 1..EMPLOYEES loop
      for Day in 1..DAYS loop
         Hours_Worked(Emp_Number, Day) := 8;
      end loop;
   end loop;
   Hours_Worked(2, 5) := 3;
   Hours_Worked(3, 5) := 0;

   Put_Line("The Hours_Worked array is filled");

                                -- Start all parallel additions
   for Emp_Number in 1..EMPLOYEES loop
      Adding_Task(Emp_Number).Start_Sums(Emp_Number);
   end loop;

                                -- Get the results back
   for Emp_Number in 1..EMPLOYEES loop
      Adding_Task(Emp_Number).Return_Sum(Result);
      Weekly_Totals(Emp_Number) := Result;
   end loop;

   for Emp_Number in 1..EMPLOYEES loop
      Put("Employee number");
      Put(Emp_Number, 3);
      Put(" worked");
      Put(Weekly_Totals(Emp_Number), 3);
      Put_Line(" hours.");
   end loop;

end Parallel;




-- Result of execution

-- The Hours_Worked array is filled
-- Employee number  1 worked 56 hours
-- Employee number  2 worked 51 hours
-- Employee number  3 worked 48 hours
-- Employee number  4 worked 56 hours
-- Employee number  5 worked 56 hours
-- Employee number  6 worked 56 hours
-- Employee number  7 worked 56 hours
-- Employee number  8 worked 56 hours
-- Employee number  9 worked 56 hours
-- Employee number 10 worked 56 hours
-- Employee number 11 worked 56 hours

 

示例程序名为e_c29_p4。Ada是一个非常糟糕的解决这个特定问题的方法的例子,但却是一个有意义的解决问题的任务的例子。在这个程序中,我们将声明11个并行任务,开始运行这11个任务,然后检索这11个任务的结果。

我们首先声明几种类型和变量,然后在第17行到第20行中声明一个任务规范,其中有两个入口点。因为常量EMPLOYEES被声明为值11,所以我们在第22行声明了11个任务,命名为Adding_Task(1), Adding_Task(2),…Adding_Task(11),当我们开始执行主程序时,每个任务都将开始运行。任务主体(从第24行到第39行)由一个局部变量和两个accept语句组成,它们之间有一些计算。

主程序是这个程序中最有趣的部分,但是我们需要先完成初始化代码,然后才能进入有趣的部分。第42行到第48行中的初始化代码将值赋给前面声明的大数组,其中包括将在结果中显示的一些有趣的数据点。最后,显示一条消息,表示数组已被填满。

 

开始运行所有任务

第53行到第55行中的循环在11个任务的第一个入口点调用它们,并开始执行它们,以便它们都执行各自的for循环。如果您有一台具有大量实际处理器的计算机,那么每个任务都可以分配给一个单独的处理器,所有的计算都可以同时完成。在目前的加7个数的情况下,计算是微不足道的,但如果每个任务需要几百万个浮点运算的性能,通过有效使用并行硬件节省的时间可能是非常重要的。本程序旨在说明技术,可以使用,是最实际的说明任务,将被证明在本课程。

 

所有任务现在都完成了

第58行到第61行中的循环再次调用所有11个任务,每次一个,在它们的最终入口点,它请求返回求和的结果。所有结果都存储在Weekly_Totals数组中,然后将结果与适当的文本一起显示。当您检查执行结果时,您将注意到编号为2和3的员工的总数中反映了有趣的数据。

您应该从这个程序中了解到的要点是,所有11个任务都是并行操作的,主程序是第12个任务。主程序的逻辑要求并发任务,而不是对单个子程序进行11次调用。请记住,这实际上是一个非常糟糕的方法来编程这个特定的问题,但包括作为一个说明。

请确保在您的系统上编译并执行此程序,以验证结果是否相同。在它正确编译和执行之后,更改雇员的数量,以查看有多少任务可以与您的系统并行操作。您不会受到特定编译器对允许的任务数量的任意上限的限制,而是受到每个任务所需的内存数量的限制。

 

优先级的任务

Example program ------> e_c29_p5.ada

                                     -- Chapter 29 - Program 5
with Ada.Text_IO;
use Ada.Text_IO;

procedure Priority is

task type SHORT_LINE is
   pragma PRIORITY (5);
end SHORT_LINE;

task type LONG_LINE is
   pragma PRIORITY (1);
end LONG_LINE;

pragma PRIORITY (3);   -- This is the priority for the main program

Cow, Dog, Pig          : SHORT_LINE;
Elephant, Hippopotamus : LONG_LINE;

task body SHORT_LINE is
begin
   for Index in 1..4 loop
      delay 0.0;
      Put_Line("This is a short line");
   end loop;
end SHORT_LINE;

task body LONG_LINE is
begin
   for Index in 1..3 loop
      delay 0.0;
      Put_Line("This is a much longer line to be displayed");
   end loop;
end LONG_LINE;

begin
   Put_Line("This is an example of use of a task type");
end Priority;




-- Result of execution

-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is a short line
-- This is an example of use of a task type
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed
-- This is a much longer line to be displayed

 

示例程序名为e_c29_p5。Ada包含一个在任务中建立优先级的例子。任务的优先级是通过使用名为priority的pragma来指示的,如任务规范和主程序的声明部分所示。圆括号中包含一个整型类变量,较大的数字表示优先级较高或最紧急的任务。允许优先级的范围在System规范包定义中作为INTEGER的子范围为您的单个编译器定义。根据ARM,范围为0..0是合法的。如果你的编译器只支持一个值作为优先级,你将不得不在这个程序中将优先级更改为单个值,实际上,你将没有优先级。

编译并执行这个程序,然后改变优先级,看看您可以实现什么样的执行顺序。重要的是要认识到,ARM并没有绝对定义如何处理优先级,但它只给出了一个相当模糊的定义。在生产程序中应该小心使用优先级。

 

一组条目

Example program ------> e_c29_p6.ada

                                    -- Chapter 29 - Program 6
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Family is

type SPEED is (FAST, MEDIUM, SLOW);

task Selector is
   entry Answer_Query(SPEED)(Counter : INTEGER);
end Selector;

procedure Output_Value(Counter : INTEGER) is
begin
   Put(Counter, 3);
   New_Line;
end Output_Value;

task body Selector is
begin
   loop
      select
         accept Answer_Query(FAST)(Counter : INTEGER) do
            Put("FAST Query made");
            Output_Value(Counter);
         end;
      or
         when Answer_Query(FAST)'COUNT = 0 =>
         accept Answer_Query(MEDIUM)(Counter : INTEGER) do
            Put("MEDIUM Query made");
            Output_Value(Counter);
         end;
      or
         when Answer_Query(FAST)'COUNT = 0 and
              Answer_Query(MEDIUM)'COUNT = 0 =>
         accept Answer_Query(SLOW)(Counter : INTEGER) do
            Put("SLOW Query made");
            Output_Value(Counter);
         end;
      or
         terminate;
      end select;
   end loop;
end Selector;

begin

   Put_Line("Begin the main program");
   Selector.Answer_Query(FAST)(1);
   Selector.Answer_Query(FAST)(2);
   Selector.Answer_Query(SLOW)(3);
   Selector.Answer_Query(MEDIUM)(4);
   Selector.Answer_Query(SLOW)(5);
   Selector.Answer_Query(MEDIUM)(6);
   Selector.Answer_Query(FAST)(7);
   Selector.Answer_Query(FAST)(8);
   Put_Line("End of the main program");

end Family;




-- Result of Execution

-- Begin the main program
-- FAST Query made  1
-- FAST Query made  2
-- SLOW Query made  3
-- MEDIUM Query made  4
-- SLOW Query made  5
-- MEDIUM Query made  6
-- FAST Query made  7
-- FAST Query made  8
-- End of main program

示例程序名为e_c29_p6。Ada演示了另一个可以与任务集合一起使用的结构,以实现用户定义的优先级结构。在这种情况下,有三个入口点非常相似,但它们之间只有很小的区别。声明枚举变量并将其用作三个条目之间的标识符。使用条目族的唯一真正好处是它们都使用相同的名称,因此增加了结果程序的清晰度。

这里说明了条目族的用法,您应该很容易理解。当您这样做时,编译并执行这个程序,并将您的输出与执行部分的结果中给出的输出进行比较。

 

编程练习

1.增加e_c29_p2中数组的大小。Ada来查看在程序不再适合内存之前可以使这个数组达到多大。这将给您一个概念,有多少任务可以同时运行与您的系统。(Solution)

2.添加另一个访问变量e_c29_p3。并将其初始化为其声明的一部分。该任务何时开始执行?(Solution)

 

 

---------------------------------------------------------------------------------------------------------------------------

原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm

翻译(百度):博客园  一个默默的 *** 的人

posted @ 2021-04-27 22:01  yangjianfeng  阅读(93)  评论(0编辑  收藏  举报