问题
In C#, the following code (from this page) can be used to lazily instantiate a singleton class in a thread safe way:
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
lock(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}
What would be the equivalent thread safe Delphi code?
The article also mentions two problems with Double Checked Locking in Java:
- it is possible that the new object is constructed before the helper reference is made to point at the newly created object meaning that two objects are created
- it is possible that the helper reference is made to point at a block of memory while the object is still being created meaning that a reference to an incomplete object will be returned
So while the code of the C# and the Java version in the mentioned article look almost identical, only the C# version works as expected. Which leads to the additional question if these two problems also exist in a Delphi version of Double-Checked Locking?
回答1:
Use System.TMonitor to lock the object instance in a thread safe way.
function TFoo.GetHelper(): THelper;
begin
if not Assigned(FHelper) then
begin
System.MonitorEnter(Self);
try
if not Assigned(FHelper) then
FHelper := THelper.Create();
finally
System.MonitorExit(Self);
end;
end;
Result := FHelper;
end;
For further reference look at Lock my object..., please! from Allen Bauer. In fact, the rep. I gather from this should go to Allen.
回答2:
Of course, it's always worth remembering that Double-Checked Locking is Broken. This issue turns out not to apply to the x86 memory model but it's always worth bearing in mind for the future. I'm sure there will be Delphi version at some point that will run on a platform with a memory model that is afflicted by this issue.
Embarcadero have started using a lock-free version of this pattern with interlocked compare/exchange. For example:
class function TEncoding.GetUnicode: TEncoding;
var
LEncoding: TEncoding;
begin
if FUnicodeEncoding = nil then
begin
LEncoding := TUnicodeEncoding.Create;
if InterlockedCompareExchangePointer(Pointer(FUnicodeEncoding), LEncoding, nil) <> nil then
LEncoding.Free;
end;
Result := FUnicodeEncoding;
end;
I realise this isn't an answer to the question but it didn't really fit in a comment!
来源:https://stackoverflow.com/questions/4475080/how-should-double-checked-locking-be-implemented-in-delphi