混合原子操作和非原子操作
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 then
和 Result := FBase64Encoding;
是不是写错了?
您在评论中明确表示您担心未受保护的内存操作可能会撕裂。通过撕裂,我们的意思是读取线程在部分写入时读取变量。
总的来说这是一个合理的担忧,但在这种情况下不会发生撕裂。原因是对齐的内存访问保证不会撕裂。当内存操作对齐时,就像这个一样,reader 不能读取部分写入的变量。这通常由硬件总线在单个缓存行内序列化所有内存访问来保证。
所以,不,这不是错误,代码是正确的。
代码本身用于延迟创建单例。以线程安全方式执行此操作的常用技术是双重检查锁定。该代码使用了一种避免锁定的替代技术。相反,该代码可能允许多个线程推测性地创建单例。如果多个线程成功创建对象,则第一个成功的线程获胜,其他线程销毁它们的实例并使用获胜者线程创建的实例。
只要创建额外的实例然后销毁它们是良性的,无锁方法就可以很好地工作。但情况并非总是如此。例如,创建实例的多个副本可能过于昂贵。在这种情况下,基于锁的方法更好。
在 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;
是不是写错了?
您在评论中明确表示您担心未受保护的内存操作可能会撕裂。通过撕裂,我们的意思是读取线程在部分写入时读取变量。
总的来说这是一个合理的担忧,但在这种情况下不会发生撕裂。原因是对齐的内存访问保证不会撕裂。当内存操作对齐时,就像这个一样,reader 不能读取部分写入的变量。这通常由硬件总线在单个缓存行内序列化所有内存访问来保证。
所以,不,这不是错误,代码是正确的。
代码本身用于延迟创建单例。以线程安全方式执行此操作的常用技术是双重检查锁定。该代码使用了一种避免锁定的替代技术。相反,该代码可能允许多个线程推测性地创建单例。如果多个线程成功创建对象,则第一个成功的线程获胜,其他线程销毁它们的实例并使用获胜者线程创建的实例。
只要创建额外的实例然后销毁它们是良性的,无锁方法就可以很好地工作。但情况并非总是如此。例如,创建实例的多个副本可能过于昂贵。在这种情况下,基于锁的方法更好。