Entwickler-Ecke
Open Source Units - Init Once
delfiphan - So 29.08.10 00:35
Titel: Init Once
Das Singleton-Pattern thread-safe korrekt zu implementieren ist nicht ganz einfach. Es wird häufig entweder über eine CriticalSection oder über
Double Checked Locking [
http://en.wikipedia.org/wiki/Double-checked_locking] implementiert. CriticalSections sind für diesen Zweck sehr langsam und Double Checked Locking kann je nach Compiler Schwierigkeiten bereiten. Mit den neuen APIs von Vista+ kann ein thread-safe Singleton einfach implementiert werden. Genau dazu ist diese Unit da.
http://msdn.microsoft.com/en-us/library/ms683493(v=VS.85).aspx
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:
| unit InitializeOnce;
interface
type TInitializeOnce = class private FInit: Pointer; FOwnsObject: Boolean; function GetInstance: TObject; protected function DoInstantiate: TObject; virtual; abstract; public destructor Destroy; override; property OwnsObject: Boolean read FOwnsObject write FOwnsObject; property Instance: TObject read GetInstance; end;
implementation
function InitOnceExecuteOnce(var InitOnce: Pointer; Callback: Pointer; Param: Pointer; var Context: Pointer): Boolean stdcall; external 'kernel32.dll' name 'InitOnceExecuteOnce';
function InitHandleFunction(InitOnce: Pointer; Parameter: Pointer; var Context: Pointer): Boolean; stdcall; begin Context := TInitializeOnce(Parameter).DoInstantiate; Result := True; end;
destructor TInitializeOnce.Destroy; begin if FOwnsObject and (FInit <> nil) then Instance.Free; inherited; end;
function TInitializeOnce.GetInstance: TObject; begin InitOnceExecuteOnce(FInit, @InitHandleFunction, Pointer(Self), Pointer(Result)); end;
end. |
Marc. - So 29.08.10 16:08
Hi!
Delphi-Quelltext
26: 27: 28: 29: 30:
| { ... } function InitHandleFunction(InitOnce: Pointer; Parameter: Pointer; var Context: Pointer): Boolean; stdcall; begin Context := TInitializeOnce(Parameter).DoInstantiate; Result := True; end; |
Was genau bringt Result := True hier, außer den Compiler zu besänftigen, da die Funktion einen Rückgabewert erwartet?
Die Original-Funktion sieht, wenn ich das richtig recherchiert habe, so aus:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| BOOL CALLBACK InitHandleFunction ( PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext) {
HANDLE hEvent;
hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
if (NULL == hEvent) { return FALSE; } else { *lpContext = hEvent; return TRUE; } } |
Hier wird die Hilfsvariable hEvent zunächst auf NULL überprüft; je nach Ergebnis wird Context deren Wert anschließend zugewiesen.
Sollte es daher nicht in etwa wie folgt lauten:
Delphi-Quelltext
1: 2: 3: 4: 5:
| function InitHandleFunction(InitOnce: Pointer; Parameter: Pointer; var Context: Pointer): Boolean; stdcall; begin Context := TInitializeOnce(Parameter).DoInstantiate; Result := Context <> nil; end; |
Beste Grüße,
Marc
delfiphan - So 29.08.10 16:31
Hi! Jo, ginge auch, ist vielleicht sogar ein bisschen besser, danke.
Bei Delphi erwarte ich bei einem Fehler in einer Factorymethode eher eine Exception, daher wird immer True zurückgegeben (oder eben eine Exception)
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!