在障碍条件中使用受保护的条目参数
Using protected entry argument in barrier condition
我有一个受保护的 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;
如果你例子中的map key确实是有限范围内的某个离散值,那么@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.[=28 中进行了讨论=]
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);
Protected_Map.Map.Get (1, 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
我有一个受保护的 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;
如果你例子中的map key确实是有限范围内的某个离散值,那么@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.[=28 中进行了讨论=]
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);
Protected_Map.Map.Get (1, 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