views:

133

answers:

1

As I known, there are two common kinds of practices to ensure the thread safety of lazy-initialization:

  1. Double-checked locking (Marks the variable as volatile to avoid the memory ordering)
  2. InterlockedCompareExchangePointer

It seems VCL uses the second practice. Is there any reason?

class function TEncoding.GetUTF8: TEncoding;
var
  LEncoding: TEncoding;
begin
  if FUTF8Encoding = nil then
  begin
    LEncoding := TUTF8Encoding.Create;
    if InterlockedCompareExchangePointer(Pointer(FUTF8Encoding), LEncoding, nil) <> nil then
      LEncoding.Free;
  end;
  Result := FUTF8Encoding;
end;

or is there any better method?

Thanks!

+3  A: 

There should not be much speed difference. In both approaches, global field is first checked if it is initialized and initialization is performed only when required. Therefore, most of the time the function will just do compare, jump, move, without any initialization.

When initialization is performed, InterlockedCompareEtc has two advantages over locking.

  1. It is faster.
  2. The code is shorter (no need to initialize a lock etc).

I find the InterlockedCompareEtc approach "neater" and use it in my code. But the locking would work equally well.

gabr
I dislike code duplication, so I got some ideas that may simplify the code above:1. When the param type "T" has the default constructor Result := TUtils.GetInstance<TEncoding>(FUTF8Encoding, TUTF8Encoding);2. Use an anonymous method to create a local instance. Result := TUtils.GetInstance<TEncoding>(FUTF8Encoding, function: TEncoding begin Result := TUTF8Encoding.Create; end);How about that?
Paul
You should edit your original post, Paul, instead of writing the comment.
gabr