将原子操作与非原子操作混合

lok*_*oki 5 delphi firemonkey

在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;
Run Code Online (Sandbox Code Playgroud)

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

这不是一个错误吗?

Dav*_*nan 12

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

这是一个普遍有效的问题,但在这种情况下不会发生撕裂.原因是保证对齐的内存访问不会撕裂.当对齐存储器操作时,读者无法读取部分写入的变量.这通常由硬件总线在单个高速缓存行内序列化所有存储器访问来保证.

所以,不,这不是一个错误,代码是正确的.

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

如果创建其他实例然后销毁它们是良性的,那么无锁方法很有效.但情况并非总是如此.例如,创建实例的多个副本可能太昂贵.在这种情况下,基于锁定的方法更好.