从另一个线程中设置线程中的布尔值是否安全?

Mar*_*ner 9 delphi multithreading thread-safety

我想知道以下(伪)代码是否可以安全使用.我知道Terminated标志但我需要在主线程的递归搜索操作中设置某种取消标志并保持工作线程运行.我将在那里检查Terminated属性,这个伪代码中缺少什么.

type
  TMyThread = class(TThread)
  private
    FCancel: Boolean;
    procedure RecursiveSearch(const ItemID: Integer);
  protected
    procedure Execute; override;
  public
    procedure Cancel;
end;

procedure TMyThread.Cancel;
begin
  FCancel := True;
end;

procedure TMyThread.Execute;
begin
  RecursiveSearch(0);
end;

procedure TMyThread.RecursiveSearch(const ItemID: Integer);
begin
  if not FCancel then
    RecursiveSearch(ItemID);  
end;

procedure TMainForm.ButtonCancelClick(Sender: TObject);
begin
  MyThread.Cancel;
end;
Run Code Online (Sandbox Code Playgroud)

以这种方式在线程内部设置布尔属性FCancel是否安全?当按下主窗体(主线程)中的按钮时,这不会与RecursiveSearch过程中读取此标志相冲突吗?或者我是否必须添加例如关键部分来读取和写入此值?

非常感谢

Dav*_*nan 17

这样做非常安全.读取线程将始终读为true或false.不会撕裂,因为a Boolean只是一个字节.事实上,对于32位进程中的对齐32位值也是如此,即Integer.

这就是所谓的良性种族.布尔变量存在竞争条件,因为一个线程在另一个线程写入时读取,而没有同步.但该计划的逻辑并未受到竞赛的不利影响.在更复杂的情况下,这样的竞赛可能是有害的,然后需要同步.


dth*_*rpe 8

从不同的线程写入布尔字段是线程安全的 - 这意味着,写操作是原子的.当该值被写入该字段时,该字段的观察者将不会看到"部分值".对于较大的数据类型,部分写入是实际可能的,因为需要多个CPU指令将值写入字段.

因此,布尔值的实际写入不是线程安全问题.但是,观察者如何使用该布尔字段可能是线程安全问题.在您的示例中,唯一可见的观察者是RecursiveSearch函数,并且它对FCancel值的使用非常简单且无害.FCancel状态的观察者不会改变FCancel状态,因此这是一个直接/非循环的生产者 - 消费者类型依赖关系.

如果相反代码使用布尔字段来确定是否需要执行一次性操作,对布尔字段的简单读取和写入将是不够的,因为布尔字段的观察者也需要修改字段(以标记一次性操作已经完成).这是一个读 - 修改 - 写周期,当两个或多个线程在恰当的时间执行相同的步骤时,这是不安全的.在这种情况下,您可以在一次性操作(和布尔字段检查和更新)周围放置互斥锁,或者您可以使用InterlockedExchange更新和测试布尔字段而不使用互斥锁.您还可以将一次性操作移动到静态类型构造函数中,而不必自己维护任何锁(尽管.NET可能会在幕后使用锁).

  • 你是什​​么意思没有.NET?.NET是用Delphi编写的!:P (4认同)