混合原子操作和非原子操作

Mixing Atomic operation with non atomic operation

在 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
{$IFDEF AUTOREFCOUNT}
    else
      FBase64Encoding.__ObjAddRef
{$ENDIF AUTOREFCOUNT};
  end;
  Result := FBase64Encoding;
end;

但我不明白,他们混合了原子操作AtomicCmpExchange(Pointer(FBase64Encoding), Pointer(LEncoding), nil)非原子操作,比如if FBase64Encoding = nil thenResult := FBase64Encoding;

是不是写错了?

您在评论中明确表示您担心未受保护的内存操作可能会撕裂。通过撕裂,我们的意思是读取线程在部分写入时读取变量。

总的来说这是一个合理的担忧,但在这种情况下不会发生撕裂。原因是对齐的内存访问保证不会撕裂。当内存操作对齐时,就像这个一样,reader 不能读取部分写入的变量。这通常由硬件总线在单个缓存行内序列化所有内存访问来保证。

所以,不,这不是错误,代码是正确的。

代码本身用于延迟创建单例。以线程安全方式执行此操作的常用技术是双重检查锁定。该代码使用了一种避免锁定的替代技术。相反,该代码可能允许多个线程推测性地创建单例。如果多个线程成功创建对象,则第一个成功的线程获胜,其他线程销毁它们的实例并使用获胜者线程创建的实例。

只要创建额外的实例然后销毁它们是良性的,无锁方法就可以很好地工作。但情况并非总是如此。例如,创建实例的多个副本可能过于昂贵。在这种情况下,基于锁的方法更好。