如何解决在屏障条件下使用受保护的条目参数
我有一个带有数据向量的受保护 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;
main.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 举报,一经查实,本站将立刻删除。