在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;
这不是错误吗?
答案 0 :(得分:12)
在注释中您清楚地表明,您担心的是未受保护的内存操作可能会中断。撕裂是指读取线程在部分写入变量时读取变量。
通常,这是一个有效的问题,但是在这种情况下不会发生撕裂。原因是保证对齐的内存访问不会中断。对齐存储操作时,阅读器无法读取部分写入的变量。通常通过硬件总线在单个缓存行中序列化所有内存访问来保证这一点。
所以,不,这不是一个错误,代码是正确的。
代码本身用于延迟创建单例。以线程安全的方式执行此操作的常用技术是双重检查锁定。该代码使用了另一种避免锁定的技术。相反,该代码潜在地允许多个线程推测性地创建单例。如果有多个线程成功创建对象,则第一个成功的对象将获胜,其他线程将销毁其实例并使用获胜者线程创建的实例。
如果创建其他实例然后销毁它们,则无锁方法很好用。但这并非总是如此。例如,创建实例的多个副本可能太昂贵。在这种情况下,基于锁定的方法会更好。