【问题标题】:Delphi static array thread safety only with critical sectionDelphi 静态数组线程安全仅在临界区
【发布时间】:2012-10-10 11:36:42
【问题描述】:

我有一个 Win32 线程(没有 TThread),它一直在运行并遍历一个静态数组。主线程可以修改数组的字段。 没有像 TThreadList 之类的组件(对于无 vcl 应用程序),只使用 Windows 关键部分 (TRTLCriticalSection) 的最佳方法是什么?

代码:

type
  T = record
    Idx: Integer;
    Str: string;
    Num: Real;
    Enabled: Boolean;
  end;

var
  A: Array[0..9] of T;
  Cnt: Integer;
  CS: TRTLCriticalSection;

procedure thread;
var
  I: Integer;
begin
  while True do
  begin
    for I := Low(A) to High(A) do
    begin
      if A[I].Enabled then
      begin
        //modify some fields from A[I]

        Inc(A[I].Idx);
        if A[I].Idx >= 10 then
        begin
          A[I].Enabled := False;
          InterlockedDecrement(Cnt);
        end;
      end;
    end;
    if Cnt = 0 then Sleep(1);
  end;
end;

procedure Add(...); //called only from mainthread

  function GetFreeField: Integer;
  begin
    for Result := Low(A) to High(A) do
      if not A[Result].Enabled then Exit;
    Result := -1;
  end;

var
  I: Integer;
begin
  I := GetFreeField;
  if I = -1 then Exit;

  //set fields A[I]

  A[I].Enabled := True;
  InterlockedIncrement(Cnt);
end;

一开始,数组被初始化为 enabled = false 和 cnt = 0。

下面的修改够吗?

procedure thread;
var
  I: Integer;
begin
  while True do
  begin
    for I := Low(A) to High(A) do
    begin
      EnterCriticalSection(CS);
      if A[I].Enabled then
      begin
        LeaveCriticalSection(CS);
        //modify some fields from A[I]

        Inc(A[I].Idx);
        if A[I].Idx >= 10 then
        begin
          EnterCriticalSection(CS);
          A[I].Enabled := False;
          LeaveCriticalSection(CS);

          InterlockedDecrement(Cnt);
        end;
      end
      else
        LeaveCriticalSection(CS);
    end;
    if Cnt = 0 then Sleep(1);
  end;
end;

procedure Add(...); //called only from mainthread
var
  I: Integer;
begin
  I := GetFreeField;
  if I = -1 then Exit;

  //set fields A[I]

  EnterCriticalSection(CS);
  A[I].Enabled := True;
  LeaveCriticalSection(CS);

  InterlockedIncrement(Cnt);
end;

【问题讨论】:

  • TThreadList 不是VCL 的一部分,它在 System.Classes 中。在这样的列表中添加/删除项目是线程安全的。不过,操作列表项必须受到保护。

标签: multithreading delphi critical-section static-array


【解决方案1】:

在我看来你的设计是这样的:

  1. 主线程只会将Enabled 标志从False 切换到True
  2. 工作线程只在相反方向切换标志。
  3. 除了我们在此处看到的代码之外,没有其他代码可以访问该数组。

如果是这样,那么没有临界区的原始代码已经是线程安全的。至少它是在使用强内存模型的硬件上。例如 Intel x86 或 x64 架构。 Enabled 布尔值充当线程之间的同步屏障。

但是,您的整个设计在我看来是有缺陷的。 while True 循环和Sleep 让我有些警觉。该线程无缘无故地重复运行。当然,您应该只在主线程对数组进行修改时才在线程中执行代码。我更喜欢使用信号(例如 Windows 事件)来唤醒线程。

【讨论】:

  • 读/写字节(布尔值)是英特尔处理器上的保证原子操作;现代处理器不支持强内存排序,这是一个不同的概念,与原始问题无关。
  • @Serg 如果像这样的代码在弱内存模型下运行,那么布尔写入可以在修改到 A[i] 字段之前重新排序。当然,它不会在弱内存模型下运行。并且 x86 和 x64 具有强大的内存模型。
  • 原始代码不完整,不清楚,我看不出到底应该保护什么,所以很难说它的线程安全性。
  • @Serg 我们必须假设,正如我在答案中的第 3 点所做的那样,没有其他代码访问A。有了这个假设,就很容易分析了。
  • 如果是这样,那么就没有什么需要保护的了,线程安全也没有任何问题,即使A[I].Enabled := True/False 不是原子操作,但代码“原样”对我来说毫无意义。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-22
  • 2015-03-24
相关资源
最近更新 更多