代码之家  ›  专栏  ›  技术社区  ›  zeus

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

  •  5
  • zeus  · 技术社区  · 5 年前

    在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 then Result := FBase64Encoding;

    这不是一个错误吗?

    1 回复  |  直到 5 年前
        1
  •  12
  •   David Heffernan    5 年前

    在注释中,您清楚地表明您的问题是未保护的内存操作可能会被破坏。撕裂意味着读线程在部分写入变量时读取变量。

    一般来说,这是一个有效的问题,但是在这种情况下,撕裂是不可能发生的。原因是对齐的内存访问保证不会中断。当内存操作对齐时,与此操作一样,读卡器无法读取部分写入的变量。这通常由硬件总线在一条缓存线内串行化所有内存访问来保证。

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

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

    如果创建额外的实例然后销毁它们是有益的,那么无锁方法可以很好地工作。但情况并非总是如此。例如,创建实例的多个副本可能太贵。在这种情况下,基于锁的方法更好。