如何在 ada 包中启动单一实例任务



这个星期天我正在做一些Ada.. ;-)

我写了一个小的日志包:

日志广告:

package Log is
procedure log (text: String);
end Log;

log.adb:

with Ada.Text_IO;
package body Log is
procedure log (text: String) is
begin
Ada.Text_IO.Put (text);
end log;
end Log;

我可以这样使用它: test.adb:

with Log;
procedure Test is
begin
Log.log ("bla bla");
end Test;

现在,我想"改进"这个包。我希望日志过程将文本"推送"到任务。这是执行"Ada.Text_IO"的任务。把(文本("。 任务可以是:

task Logger_Task is
entry log (text : String);
end Logger_Task;
task body Logger_Task is
begin
loop
accept log (text: String) do
Ada.Text_IO.Put (text);
end log;
end loop;
end Logger_Task;

我希望日志客户端不知道此任务,因此它应该隐藏在日志包中的某个位置。 我不知道如何以及在何处实例化任务...

此任务还必须在整个应用程序期间保持活动状态。

感谢您的帮助。

你已经实例化了它。

task Logger_Task is
entry log (text : String);
end Logger_Task;

与创建匿名task type的实例相同:

task type Anonymous is
entry log (text : String);
end Anonymous;
Logger_Task : Anonymous;

如果在包的正文中定义任务,则可以保留procedure log接口:

with Ada.Text_IO.Unbounded_IO;
with Ada.Strings.Unbounded;
package body Log is
task Logger_Task is
entry log (text : String);
end Logger_Task;
task body Logger_Task is
Cache: Ada.Strings.Unbounded.Unbounded_String;
begin
loop
select
accept log (text: String) do
Cache := Ada.Strings.Unbounded.To_Unbounded_String (text);
end log;
Ada.Text_IO.Unbounded_IO.Put (Cache);
or
terminate;
end select;
end loop;
end Logger_Task;
procedure log (text: String) is
begin
Logger_Task.log (text);
end log;
end Log;

select … or terminate;对于在主任务结束时结束任务至关重要(只有在主任务结束时才会采用此替代方案(。

Cache也很重要,因为它允许调用任务在accept块收到text参数后继续。直接调用接受块中的Put将使调用任务等待它完成,因为它只有在离开accept块后才会继续。

另一个用户展示了使用会合的方法,但如果处理该消息的方法可以阻止,那么即使您在 accept 语句之外执行此操作,调用方仍可能阻止等待记录器任务返回 accept 语句。 Text_IO应该不是什么大问题,但对于可能阻塞更长时间的其他类型的输出,请考虑替代方案:

使用与独立任务配对的无限同步队列,该任务监视队列并在添加消息时从中检索消息并且有时间。 即使它是一个条目,排队操作也不会阻止调用任务。这样,如果记录器的输出方法最终偶尔阻塞相对较长的时间段,则其他任务只会在队列中弹出消息并继续前进,监视任务最终将在赶上时输出它们。

logging.ads

package Logging is
procedure Put_Line(Message : String);
end Logging;

logging.adb

with Ada.Containers.Synchronized_Queue_Interfaces;
with Ada.Containers.Unbounded_Synchronized_Queues;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
use Ada.Containers;
with Ada.Task_Identification; use Ada.Task_Identification;
with Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO;
package body Logging is
-- Create the queue
package Interfaces is new Synchronized_Queue_Interfaces(Unbounded_String);
package Queues is new Unbounded_Synchronized_Queues(Interfaces);
Messages : Queues.Queue;
-- This task will monitor the queue until the program ends
task Logger;
task body Logger is
Message : Unbounded_String := Null_Unbounded_String;
begin
-- Stop looping once both main ends and there are no messages left
-- The Environment_Task refers to the task that runs Main
while Is_Callable(Environment_Task) or Messages.Current_Use > 0 loop
-- Check the queue for a message but time out if not.  We
-- need the timeout because Dequeue can block if the queue 
-- is empty.
select
Messages.Dequeue(Message);
Ada.Text_IO.Unbounded_IO.Put_Line(Message);
or
-- Pick a timeout that makes sense.  Longer timeouts 
-- give better CPU usage during idle times, but also means
-- the program might not end immediately. 10ms or 1 second
-- might be fine for general use
delay 1.0;
end select;
end loop;
Ada.Text_IO.Put_Line("Main task closed, closing Logger");
end Logger;
procedure Put_Line(Message : String) is 
begin
Messages.Enqueue(To_Unbounded_String(Message));
end Put_Line;
end Logging;

最新更新