访问线程使用的对象时减少开销

时间:2015-06-28 18:19:10

标签: multithreading delphi freepascal lazarus

一个线程每秒循环遍历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。

2 个答案:

答案 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