我希望这个问题不是太广泛。
我正在使用传统的Ada应用程序。这个应用程序是围绕一个非常古老的中间件构建的,它可以处理我们的IPC。为了这个问题,我可以将中间件的规定归结为
1:处理消息的消息循环(来自其他程序或此程序)
2:向此程序或其他人发送消息的功能
3:从数据库中读取的函数
该程序主要在消息循环上运行 - 简单地说就像
loop
This_Msg := Message_Loop.Wait_For_Message; -- Blocking wait call
-- Do things based on This_Msg's ID
end loop
然而,也存在可以由外部刺激触发的回调。这些回调在他们自己的线程中运行。其中一些回调调用了数据库读取功能,除了我们最近发现的,在一个相对罕见的情况下,它一直很好。当这种情况发生时,事实证明当消息循环执行其阻塞Wait_For_Message时,从数据库读取是不安全的。
似乎一个简单的解决方案是使用受保护对象来同步Wait_For_Message和数据库读取:如果我们在Wait_For_Message阻塞时尝试读取数据库,则读取将阻塞,直到Wait_For_Message返回,此时Wait_For_Message调用将被阻止,直到数据库读取完成。下一个问题是我无法保证消息循环会及时收到消息,这意味着数据库读取可能会被阻塞一段任意时间。看起来这个解决方案也很简单:在阻塞之前向循环发送do-nothing消息,确保Wait_For_Message调用将产生。
我试图包围的是:
如果我在数据库读取之前发送了do-nothing消息和THEN阻止,我不认为我可以保证Wait_For_Message不会返回,产生,处理do-nothing消息,并启动在数据库前读取块之前再次阻塞。我认为我在概念上需要开始阻止,然后推送消息,但我不知道该怎么做。我想我可以通过第二层锁来处理它,但我无法想到最有效的方法,并且不知道这是否是正确的解决方案。这是我第一次涉足Ada的并发性,所以我希望指向正确的方向。
答案 0 :(得分:0)
也许你应该为此使用一项任务;以下将使任务在SELECT
等待处理消息或访问数据库,而在处理期间对条目的另一次调用将在该条目上排队以使循环重复select
,从而完全消除这个问题...除非你的DB访问以某种方式调用消息条目;但这不应该发生。
Package Example is
Task Message_Processor is
Entry Message( Text : String );
Entry Read_DB( Data : DB_Rec );
End Message_Processor;
End Example;
Package Body Example is
Task Body Message_Processor is
Package Message_Holder is new Ada.Containers.Indefinite_Holders
(Element_Type => String);
Package DB_Rec_Holder is new Ada.Containers.Indefinite_Holders
(Element_Type => DB_Rec);
Current_Message : Message_Holder.Holder;
Current_DB_Rec : DB_Rec_Holder.Holder;
Begin
MESSAGE_LOOP:
loop
select
accept Message (Text : in String) do
Current_Message:= Message_Holder.To_Holder( Text );
end Message;
-- Process the message **outside** the rendevouz.
delay 1.0; -- simulate processing.
Ada.Text_IO.Put_Line( Current_Message.Element );
or
accept Read_DB (Data : in DB_Rec) do
Current_DB_Rec:= DB_Rec_Holder.To_Holder( Data );
end Message;
-- Process the DB-record here, **outside** the rendevouz.
or
Terminate;
end select;
end loop MESSAGE_LOOP;
End Message_Processor;
End Example;