我在 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;
我关心的是这段代码的多线程行为。具体来说,同时执行此函数的两个线程之间是否存在潜在的竞争条件?例如,考虑以下场景:
在这种情况下,是否有可能 FBase64Encoding 不是 nil,但仍然不是完全有效的指针,因为 AtomicCmpExchange 尚未完成将值写入 FBase64Encoding?
任何关于此代码是否会导致竞争条件的见解,如果是的话,如何缓解它,将不胜感激。
相关代码是线程安全的。
有可能两个或多个线程会通过
nil
比较:
if FBase64Encoding = nil then
在这种情况下,多个线程将构造
TBase64Encoding
实例并将该实例分配给局部变量 LEncoding
。
但是,只有其中一个线程能够成功将本地
LEncoding
变量中的值分配给 FBase64Encoding
字段,因为 AtomicCmpExchange
将自动执行比较和赋值。这就是 AtomicXXX
函数的全部目的。
在一个线程将值分配给
FBase64Encoding
后,AtomicCmpExchange
对于所有其他线程都将失败(它将返回 FBase64Encoding
的当前内容,在这种情况下将不是 nil
值),然后构造编码实例不需要的线程将通过 LEncoding.Free
释放。
这段代码的唯一后果是,可能会构造一些额外的编码实例,而那些不需要的实例将被立即释放。
如果您在代码中使用这种延迟初始化,您需要注意创建的实例不会占用太多资源,更重要的是,它们可以正确构造而不会引发异常,否则可能会对发生异常的线程中的代码。
原子内在比较和交换功能比较内容 目标到给定值(比较),并且仅当它们是 同样,将Target的内容修改为新值。
该函数始终返回 Target 的原始值。如果一个 提供了Succeeded参数,如果有则Succeeded变为True 价值交换(即使 Target 和 NewValue 相同);它 否则变为 False。
System 单元提供了四个原子内部函数,这些函数提供 一种原子交换、比较和交换、增量和的方法 减少内存值。
AtomicCmpExchange 用于比较和交换内存值。
原子操作用于:
- 实现多线程锁定原语
- 提供实现所谓的“无锁”结构所需的原语