这个星期天我正在做一些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;