Delphi,安全快速地访问静态数组的部分元素
我有100项记录类型的静态数组:Delphi,安全快速地访问静态数组的部分元素,delphi,thread-safety,critical-section,Delphi,Thread Safety,Critical Section,我有100项记录类型的静态数组: TMy_Array:array[1..100] of T; 式中,T为: T = record A: double; B: Date; C: String; end; 我有n类似的线程修改TMy\u数组中的元素 TMy_Array[n].A; TMy_Array[n].B; TMy_Array[n].C)`. N接近100 我还有10个其他线程,它们选择性地修改任何字段TMy\u数组。 为了安全地实现这一点,我应该使用关键部分,问题是:
TMy_Array:array[1..100] of T;
式中,T为:
T = record
A: double;
B: Date;
C: String;
end;
我有n
类似的线程修改TMy\u数组中的元素
TMy_Array[n].A;
TMy_Array[n].B;
TMy_Array[n].C)`.
N
接近100
我还有10个其他线程,它们选择性地修改任何字段TMy\u数组
。
为了安全地实现这一点,我应该使用关键部分,问题是:
仅使用一个关键部分是否不会导致战斗线程和等待线程过度拥挤以访问阵列
也许我可以或者应该应用100个关键部分来保护对T_数组中特定项的访问,并且不会阻止其他线程
Var
Cs:array [1..100] of TRTLCriticalSection;
//then for n-thread:
EnterCriticalSection(CS[n]);
TMy_Array[n].A:=…;
TMy_Array[n].B:=…;
TMy_Array[n].C:=…;
LeaveCriticalSection(CS[n]);
这是一个好主意,不会使应用程序过载吗
第二个问题:
TDateTime
(真的是Double
)和String
原子数据吗?我是否可以读取(仅读取)它们而不必担心在另一个线程写入时发生冲突?尝试序列化对列表/数组的访问
- 最简单的序列化方法是使用
TThreadList
保存记录。
TThreadList
有两个版本,一个在Generics.Collections
中为泛型,另一个在Classes
中为非泛型。所有访问此类列表的权限都由锁定/解锁机制保护。
这是一个良好的开端。衡量性能,并查看是否存在任何问题瓶颈
- 另一种方法是让一个线程通过线程安全队列保护所有列表/数组访问。
试图从列表/数组中读/写数据的其他线程在队列上发送读/写请求
- 对于读取请求,记录的副本在另一个队列中发送到请求线程
- 写入请求由守护线程提交
现在一切都是事件驱动的,延迟最小。线程安全没有冲突,因果关系描述清晰
对于线程安全队列,如果您有Delphi-XE2或更新版本,请查看TThreadedQueue
下面是一个概述上述队列方法的示例
Uses
Classes,SysUtils,Generics.Collections;
Type
T = record
A : Double;
B : String;
end;
var
MyArr : array[1..100] of T;
GuardingQueue : TThreadedQueue<TProc>;
procedure GuardingThread.Execute;
var
aProc : TProc;
begin
while not Terminated do
begin
aProc := GuardingQueue.PopItem;
if not Assigned(aProc) then
Exit; // Quit thread when nil is sent to the queue
aProc(); // Execute request
end;
end;
procedure AccessingThread.Execute;
var
aLocalQueue : TThreadedQueue<T>;
aT : T;
begin
// Make sure aLocalQueue is initialized
// To get data fom the array ...
GuardingQueue.PushItem( // read from array
procedure
var
aT : T;
begin
aT.A := MyArr[2].A;
aT.B := MyArr[2].B;
aLocalQueue.PushItem(aT);
end
);
aT := aLocalQueue.PopItem; // Here is the result from the read request
// Writing to the array ...
GuardingQueue.PushItem( // write to array
procedure
begin
MyArr[2].A := 2;
MyArr[2].B := 'Test';
end
);
end;
使用
类、sysutil、泛型.Collections;
类型
T=记录
A:双份;
B:弦;
结束;
变量
MyArr:T的数组[1..100];
GuardingQueue:TThreadedQueue;
程序GuardingThread.Execute;
变量
aProc:TProc;
开始
虽然没有终止
开始
aProc:=GuardingQueue.PopItem;
如果未分配(aProc),则
退出;//将nil发送到队列时退出线程
aProc();//执行请求
结束;
结束;
过程AccessingThread.Execute;
变量
aLocalQueue:TThreadedQueue;
aT:T;
开始
//确保aLocalQueue已初始化
//要获取阵列的数据。。。
GuardingQueue.PushItem(//从数组读取
程序
变量
aT:T;
开始
aT.A:=MyArr[2].A;
aT.B:=MyArr[2].B;
aLocalQueue.PushItem(aT);
结束
);
aT:=aLocalQueue.PopItem;//下面是读取请求的结果
//正在写入数组。。。
GuardingQueue.PushItem(//写入数组
程序
开始
MyArr[2].A:=2;
MyArr[2].B:='Test';
结束
);
结束;
尝试序列化对列表/数组的访问
- 最简单的序列化方法是使用
TThreadList
保存记录。
TThreadList
有两个版本,一个在Generics.Collections
中为泛型,另一个在Classes
中为非泛型。所有访问此类列表的权限都由锁定/解锁机制保护。
这是一个良好的开端。衡量性能,并查看是否存在任何问题瓶颈
- 另一种方法是让一个线程通过线程安全队列保护所有列表/数组访问。
试图从列表/数组中读/写数据的其他线程在队列上发送读/写请求
- 对于读取请求,记录的副本在另一个队列中发送到请求线程
- 写入请求由守护线程提交
现在一切都是事件驱动的,延迟最小。线程安全没有冲突,因果关系描述清晰
对于线程安全队列,如果您有Delphi-XE2或更新版本,请查看TThreadedQueue
下面是一个概述上述队列方法的示例
Uses
Classes,SysUtils,Generics.Collections;
Type
T = record
A : Double;
B : String;
end;
var
MyArr : array[1..100] of T;
GuardingQueue : TThreadedQueue<TProc>;
procedure GuardingThread.Execute;
var
aProc : TProc;
begin
while not Terminated do
begin
aProc := GuardingQueue.PopItem;
if not Assigned(aProc) then
Exit; // Quit thread when nil is sent to the queue
aProc(); // Execute request
end;
end;
procedure AccessingThread.Execute;
var
aLocalQueue : TThreadedQueue<T>;
aT : T;
begin
// Make sure aLocalQueue is initialized
// To get data fom the array ...
GuardingQueue.PushItem( // read from array
procedure
var
aT : T;
begin
aT.A := MyArr[2].A;
aT.B := MyArr[2].B;
aLocalQueue.PushItem(aT);
end
);
aT := aLocalQueue.PopItem; // Here is the result from the read request
// Writing to the array ...
GuardingQueue.PushItem( // write to array
procedure
begin
MyArr[2].A := 2;
MyArr[2].B := 'Test';
end
);
end;
使用
类、sysutil、泛型.Collections;
类型
T=记录
A:双份;
B:弦;
结束;
变量
MyArr:T的数组[1..100];
GuardingQueue:TThreadedQueue;
程序GuardingThread.Execute;
变量
aProc:TProc;
开始
虽然没有终止
开始
aProc:=GuardingQueue.PopItem;
如果未分配(aProc),则
退出;//将nil发送到队列时退出线程
aProc();//执行请求
结束;
结束;
过程AccessingThread.Execute;
变量
aLocalQueue:TThreadedQueue;
aT:T;
开始
//确保aLocalQueue已初始化
//要获取阵列的数据。。。
GuardingQueue.PushItem(//从数组读取
程序
变量
aT:T;
开始
aT.A:=MyArr[2].A;
aT.B:=MyArr[2].B;
aLocalQueue.PushItem(aT);
结束
);
aT:=aLocalQueue.PopItem;//下面是读取请求的结果
//正在写入数组。。。
GuardingQueue.PushItem(//写入数组
程序
开始
MyArr[2].A:=2;
MyArr[2].B:='Test';
结束
);
结束;
您没有提到您的Delphi版本。更高版本(D2009)有TThreadList
,它具有列表的内置线程锁定。我建议您先试试这是否足以满足您的申请。在任何给定时间,只有一个线程可以在此列表上操作。关于原子操作。从这里引用@dThorpe:。“如果您具有多值语义(在同一操作中需要更新的多个数据段),并且您的设计需要更新,那么原子写入就没有意义