一个线程每秒循环遍历1000个对象的列表。 对象包含简单的配置数据。当满足某些条件时,工作线程将获得配置数据,并根据该线程进行一些工作。
现在我想打开一个包含配置数据的设置对话框,这样我就可以更改这个对象中的数据了。但是,当线程也在不断访问它们时,我必须访问列表中的对象。我知道如何使用临界区,但如果线程每次检查一个对象时都进入临界区,那么临界区将被输入并每秒丢弃1000次。也许有更聪明的方法?
如何在以下情况下以最少的开销制作线程安全:
a)将配置数据加载到设置对话框窗体中(在虚拟模式下使用TListView,需要按需访问对象列表)
b)并将表单输入保存回对象?
编辑:要求提供更多详细信息。对象在TList中,基本上如下所示:
TConfigData = class
ID:Integer;
Name: String;
SwitchTime: TDateTime;
end;
需要将ConfigData对象的数据加载到“设置对话框”表单中以便对其进行编辑,然后,如果用户单击“确定”,则应更新ConfigData对象,并且线程将很乐意下次使用此新数据访问obkect。但是,更新不得在线程正在读取ConfigData对象的同时发生。
编辑2:其他细节:
线程正在读取ID,Name和SwitchTime,但只有线程更改了SwitchTime。 (完成工作后,计算新时间,这就是触发下一个工作事件的原因)。
设置对话框可以更改Name和SwitchTime,但不能更改ID。
答案 0 :(得分:3)
经过一番思考后,只需使用InterlockedExchangePointer即可完全不使用关键部分:
您需要添加例程来更新项目的配置:
procedure TMyThread.UpdateConfig(const aIndex: Integer; const aID:Integer;
const aName: String; const aSwitchTime: TDateTime);
var
newConfigToEdit: TConfigData;
oldValue: TConfigData;
begin
newConfigToEdit := TConfigData.Create;
newConfigToEdit.ID := aID;
newConfigToEdit.Name := aName;
newConfigToEdit.SwitchTime := aSwitchTime;
repeat
oldvalue := InterlockedExchangePointer(FConfigDataList.List[aIndex], newConfigToEdit);
until (oldvalue <> nil) and (oldValue <> newConfigToEdit);
oldvalue.Free; // Free the replaced one
end;
该例程将替换索引为aIndex
的项目的配置。要在线程中获取配置,您需要有点聪明。我们得到它的副本,并在我们处理它时用nil替换列表中的值。这可以防止其他线程更换它。完成后我们将替换后的值放回去。
procedure TMyThread.Execute;
var
configToUse: TConfigData;
begin
repeat
// Get the config and replace it with nil so it won't be changed
configToUse := InterlockedExchangePointer(FConfigDataList.List[idx], nil);
// Do stuff with the config
// Put the config back
FConfigDataList.List[idx] := configToUse;
// You could also use the line below instead of the assignment
// InterlockedExchangePointer(FConfigDataList.List[idx], configToUse);
until Terminated;
end;
如果你想用配置启动工作线程,那么你应该克隆它,然后将克隆传递给工人,因为它可以被更改。
答案 1 :(得分:-2)
主线程(伪)代码(ObjList是全局变量):
if ConfigUpdated(i) then
ObjList[i].ConfigVersion := ObjList[i].ConfigVersion + 1;
其他线程(伪)代码(ObjConfVer是线程的本地代码)
for i := 0 to ObjCount - 1 do
if ObjConfVer[i] < ObjList[i].ConfigVersion then
begin
// Here you have to take care that main thread will can not change config while you handling it
// Do something about new config
ObjConfVer[i] := ObjList[i].ConfigVersion;
// Exit from critical section
end;
如果你有n
个线程使用相同的ObjList
,那么允许每个线程独立地对改变的配置做一些事情。
顺便说一句,如果您使用FPC / Lazarus,此链接可能有用:Parallel procedures