微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在屏障条件下使用受保护的条目参数

如何解决在屏障条件下使用受保护的条目参数

我有一个带有数据向量的受保护 Hashed_Map。要从特定 Vector 获取元素,我需要将其键传递给条目,如果 Vector 为空,则等待新元素出现在其中。在屏障条件下,键参数尚不可用,我必须在带有键的过程中嵌套一个条目。在这种情况下,会出现关于可能的阻塞操作的警告。

有没有其他方法可以做到这一点?

with Ada.Containers.Vectors;
with Ada.Containers.Hashed_Maps;

package Protected_Map is

   use Ada.Containers;

   type Element_Key is new Positive;
   type Data_Type is null record;

   package Data_Vectors is new Vectors
     (Index_Type   => Natural,Element_Type => Data_Type);

   function Data_Vector_Hash
     (Key : Element_Key) return Ada.Containers.Hash_Type is
     (Hash_Type (Key));

   package Data_Vector_Maps is new Hashed_Maps
     (Key_Type        => Element_Key,Element_Type    => Data_Vectors.Vector,Hash            => Data_Vector_Hash,Equivalent_Keys => "=","="             => Data_Vectors."=");

   protected Map is

      procedure Create (Key : out Element_Key);

      procedure Put (Data : Data_Type);

      procedure Get
        (Key  : Element_Key;
         Data : out Data_Type);

      procedure Delete (Key : Element_Key);

   private

      entry Get_Element
        (Key  : Element_Key;
         Data : out Data_Type);

      Data_Vector_Map : Data_Vector_Maps.Map;

   end Map;

end Protected_Map;

解决方法

由于您的 Element_Key 是离散类型,您可以使用 条目系列(条目数组)。这里也不需要使用实际的地图,一个数组就足够了。

为了使用入口系列,您需要限制 Element_Key 的范围以适应您的实际问题(至少有一个流行的编译器将入口系列实现为实际数组,因此您很快就会用完如果范围很大,则内存)。

因此:

package Protected_Map is

   use Ada.Containers;

   type Element_Key is new Positive range 1..10; -- constrained range
   type Data_Type is null record;

   package Data_Vectors is new Vectors
     (Index_Type   => Natural,Element_Type => Data_Type);

   type Data_Vector_Array is array(Element_Key) of Data_Vectors.Vector;


   protected Map is

      procedure Put (Key : Element_Key; Data : Data_Type);

      entry Get
        (Element_Key) -- entry family
        (Data : out Data_Type);

      procedure Delete (Key : Element_Key);

   private

      Data_Vector_Map : Data_Vector_Array;

   end Map;

end Protected_Map;

和条目正文:

      entry Get
        (for Key in Element_Key) -- entry family
        (Data : out Data_Type)
        when not Data_Vector_Map(Key).Is_Empty
      is
      begin
         ...
      end Get;

然后(例如)

   for Key in Element_Key'Range loop
      Map.Get(Key)(The_Data);
   end loop;
,

如果您示例中的映射键确实是有限范围内的某个离散值,那么@egilhh 的答案确实值得考虑。如果不是这种情况,那么您可以通过使用 Get 条目和一些额外的私有 Get_Retry 条目来解决问题,如下例所示。

当您想检查某个项目(Get 条目)的可用性时使用此“模式”,如果不是,则将请求重新排队到另一个条目(Get_Retry),它会在那里等待新物品到达。该模式通常用于编写线程安全的资源管理器。

在此模式中,Get 条目始终处于启用状态(即守卫从不阻塞),因此始终允许请求进入并查看感兴趣的项目是否已经可用:

entry Get (Key : Map_Key; Data : out Data_Type)
  when True   --  Never blocking guard.
is
begin
   if Data_Available (Key) then
      Data := Data_Vector_Map (Key).Last_Element;
      Data_Vector_Map (Key).Delete_Last;
   else
      requeue Get_Retry;   -- No data available,try again later.
   end if;
end Get;

如果没有可用的项目,则请求将重新排队到 Get_Retry 条目。这个(私有)条目有一个被 Put 子程序解除阻塞的保护。如果某项通过 Put 到达,则 Put 将记录等待重试的请求数,解除对守卫的阻止,并允许挂起的请求以查看他们是否对新项感兴趣。>

procedure Put (Key : Map_Key; Data : Data_Type) is
begin
   Data_Vector_Map (Key).Append (Data);
   
   --  If there are requests for data,then record the number
   --  of requests that are waiting and open the guard of Get_Retry.
   if Get_Retry'Count /= 0 then
      Get_Retry_Requests_Left := Get_Retry'Count;
      Get_Retry_Enabled       := True;
   end if;
   
end Put;

一旦所有待处理的请求都被处理一次,Get_Retry 将禁用自身以防止再次向自身重新排队的任何请求被第二次处理。

entry Get_Retry (Key : Map_Key; Data : out Data_Type)       
  when Get_Retry_Enabled   --  Guard unblocked by Put.
is
begin
   
   --  Set guard once all pending requests have been served once.         
   Get_Retry_Requests_Left := Get_Retry_Requests_Left - 1;
   if Get_Retry_Requests_Left = 0 then
      Get_Retry_Enabled := False;
   end if;
   
   --  Check if data is available,same logic as in Get.
   if Data_Available (Key) then
      Data := Data_Vector_Map (Key).Last_Element;
      Data_Vector_Map (Key).Delete_Last;
   else
      requeue Get_Retry;   -- No data available,try again later.
   end if;
   
end Get_Retry;

注意:两个条目系列(如@egilhh 的回答中所述)以及此模式在最近的 AdaCore blogpost 中进行了讨论。

protected_map.ads

with Ada.Containers.Vectors;
with Ada.Containers.Hashed_Maps;

package Protected_Map is

   use Ada.Containers;

   type Map_Key is new Positive;
   type Data_Type is new Integer;

   function Data_Vector_Hash (Key : Map_Key) return Hash_Type is
     (Hash_Type (Key));
   
   package Data_Vectors is new Vectors
     (Index_Type   => Natural,Element_Type => Data_Type);

   package Data_Vector_Maps is new Hashed_Maps
     (Key_Type        => Map_Key,Element_Type    => Data_Vectors.Vector,Hash            => Data_Vector_Hash,Equivalent_Keys => "=","="             => Data_Vectors."=");

   protected Map is
      procedure Create (Key : Map_Key);
      procedure Delete (Key : Map_Key);
      
      procedure Put (Key : Map_Key; Data : Data_Type);      
      entry Get (Key : Map_Key; Data : out Data_Type);

   private
      
      entry Get_Retry (Key : Map_Key; Data : out Data_Type);
      
      Get_Retry_Requests_Left : Natural := 0;
      Get_Retry_Enabled       : Boolean := False;  
      
      Data_Vector_Map : Data_Vector_Maps.Map;
      
   end Map;

end Protected_Map;

protected_map.adb

package body Protected_Map is

   protected body Map is

      ------------
      -- Create --
      ------------
      
      procedure Create (Key : Map_Key) is
      begin
         Data_Vector_Map.Insert (Key,Data_Vectors.Empty_Vector);
      end Create;

      ------------
      -- Delete --
      ------------
      
      procedure Delete (Key : Map_Key) is
      begin
         Data_Vector_Map.Delete (Key);
      end Delete;
      
      ---------
      -- Put --
      ---------
      
      procedure Put (Key : Map_Key; Data : Data_Type) is
      begin
         Data_Vector_Map (Key).Append (Data);
         
         --  If there are requests for data,then record the number
         --  of requests that are waiting and unblock the guard of Get_Retry.
         if Get_Retry'Count /= 0 then
            Get_Retry_Requests_Left := Get_Retry'Count;
            Get_Retry_Enabled       := True;
         end if;
         
      end Put;
      
      --------------------
      -- Data_Available --
      --------------------
      
      function Data_Available (Key : Map_Key) return Boolean is
      begin
         return Data_Vector_Map.Contains (Key) and then
           not Data_Vector_Map (Key).Is_Empty;
      end Data_Available;
      
      ---------
      -- Get --
      ---------
      
      entry Get (Key : Map_Key; Data : out Data_Type)
        when True   --  No condition.
      is
      begin
         if Data_Available (Key) then
            Data := Data_Vector_Map (Key).Last_Element;
            Data_Vector_Map (Key).Delete_Last;
         else
            requeue Get_Retry;   -- No data available,try again later.
         end if;
      end Get;
      
      ---------------
      -- Get_Retry --
      ---------------
      
      entry Get_Retry (Key : Map_Key; Data : out Data_Type)       
        when Get_Retry_Enabled   --  Guard unblocked by Put.
      is
      begin
         
         --  Set guard once all pending requests have been served once.         
         Get_Retry_Requests_Left := Get_Retry_Requests_Left - 1;
         if Get_Retry_Requests_Left = 0 then
            Get_Retry_Enabled := False;
         end if;
         
         --  Check if data is available,same logic as in Get.
         if Data_Available (Key) then
            Data := Data_Vector_Map (Key).Last_Element;
            Data_Vector_Map (Key).Delete_Last;
         else
            requeue Get_Retry;   -- No data available,try again later.
         end if;
         
      end Get_Retry;
      
   end Map;

end Protected_Map;

ma​​in.adb

with Ada.Text_IO; use Ada.Text_IO;
with Protected_Map;

procedure Main is

   task Getter;
   
   task body Getter is
      Data : Protected_Map.Data_Type;
   begin
      Protected_Map.Map.Get (2,Data);
      Put_Line (Data'Image);
      
      Protected_Map.Map.Get (1,Data);
      Put_Line (Data'Image);
      
      Protected_Map.Map.Get (3,Data);
      Put_Line (Data'Image);
      
   end;
   
begin   
   Protected_Map.Map.Create (1);
   Protected_Map.Map.Create (2);
   Protected_Map.Map.Create (3);
   
   Protected_Map.Map.Put (1,10);
   delay 0.5;   
   Protected_Map.Map.Put (1,15);
   delay 0.5;   
   Protected_Map.Map.Put (2,20);
   delay 0.5;   
   Protected_Map.Map.Put (3,30);
   
end Main;

输出

$ ./obj/main
 20
 15
 30
 10

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?