Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi,安全快速地访问静态数组的部分元素_Delphi_Thread Safety_Critical Section - Fatal编程技术网

Delphi,安全快速地访问静态数组的部分元素

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数组。 为了安全地实现这一点,我应该使用关键部分,问题是:

我有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数组
。 为了安全地实现这一点,我应该使用关键部分,问题是:

仅使用一个关键部分是否不会导致战斗线程和等待线程过度拥挤以访问阵列

也许我可以或者应该应用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:。“如果您具有多值语义(在同一操作中需要更新的多个数据段),并且您的设计需要更新,那么原子写入就没有意义