TNetEncoding.GetBase64Encoding 中潜在的竞争条件?

问题描述 投票:0回答:1

我在 Delphi 源代码中发现了一段代码,它提出了一些有关多线程环境中线程安全性的问题。代码如下:

class function TNetEncoding.GetBase64Encoding: TNetEncoding;
var
  LEncoding: TBase64Encoding;
begin
  if FBase64Encoding = nil then
  begin
    LEncoding := TBase64Encoding.Create;
    if AtomicCmpExchange(Pointer(FBase64Encoding), Pointer(LEncoding), nil) <> nil then
      LEncoding.Free
  end;
  Result := FBase64Encoding;
end;

我关心的是这段代码的多线程行为。具体来说,同时执行此函数的两个线程之间是否存在潜在的竞争条件?例如,考虑以下场景:

  • 线程 1 到达 FBase64Encoding = nil 并发现它为 true。
  • 线程 2 执行 AtomicCmpExchange(Pointer(FBase64Encoding), Pointer(LEncoding), nil)。

在这种情况下,是否有可能 FBase64Encoding 不是 nil,但仍然不是完全有效的指针,因为 AtomicCmpExchange 尚未完成将值写入 FBase64Encoding?

任何关于此代码是否会导致竞争条件的见解,如果是的话,如何缓解它,将不胜感激。

multithreading delphi thread-safety race-condition delphi-multithreading
1个回答
0
投票

相关代码是线程安全的。

有可能两个或多个线程会通过

nil
比较:

if FBase64Encoding = nil then

在这种情况下,多个线程将构造

TBase64Encoding
实例并将该实例分配给局部变量
LEncoding

但是,只有其中一个线程能够成功将本地

LEncoding
变量中的值分配给
FBase64Encoding
字段,因为
AtomicCmpExchange
将自动执行比较和赋值。这就是
AtomicXXX
函数的全部目的。

在一个线程将值分配给

FBase64Encoding
后,
AtomicCmpExchange
对于所有其他线程都将失败(它将返回
FBase64Encoding
的当前内容,在这种情况下将不是
nil
值),然后构造编码实例不需要的线程将通过
LEncoding.Free
释放。

这段代码的唯一后果是,可能会构造一些额外的编码实例,而那些不需要的实例将被立即释放。

如果您在代码中使用这种延迟初始化,您需要注意创建的实例不会占用太多资源,更重要的是,它们可以正确构造而不会引发异常,否则可能会对发生异常的线程中的代码。

系统.AtomicCmpExchange

原子内在比较和交换功能比较内容 目标到给定值(比较),并且仅当它们是 同样,将Target的内容修改为新值。

该函数始终返回 Target 的原始值。如果一个 提供了Succeeded参数,如果有则Succeeded变为True 价值交换(即使 Target 和 NewValue 相同);它 否则变为 False。

System 单元提供了四个原子内部函数,这些函数提供 一种原子交换、比较和交换、增量和的方法 减少内存值。

AtomicCmpExchange 用于比较和交换内存值。

原子操作用于:

  • 实现多线程锁定原语
  • 提供实现所谓的“无锁”结构所需的原语
© www.soinside.com 2019 - 2024. All rights reserved.