强制消息循环产生

时间:2017-05-10 16:34:56

标签: multithreading concurrency ada

我希望这个问题不是太广泛。

我正在使用传统的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的并发性,所以我希望指向正确的方向。

1 个答案:

答案 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;