Weist Delphi die Variable zu, bevor das Objekt erstellt wird?
Weist Delphi eine Instanzvariable zu, bevor das Objekt vollständig erstellt ist?
Mit anderen Worten, eine Variable gegeben:
var
customer: TCustomer = nil;
Wir konstruieren dann einen Kunden und weisen ihn der Variablen zu:
customer := TCustomer.Create;
Ist es möglich dasscustomer
kann nicht seinnil
, aber nicht auf einen fertig konstruierten zeigenTCustomer
?
Dies wird zu einem Problem bei der Durchführung einer verzögerten Initialisierung:
function SacrifialCustomer: TCustomer;
begin
if (customer = nil) then
begin
criticalSection.Enter;
try
customer := TCustomer.Create;
finally
criticalSection.Leave;
end;
end;
Result := customer;
end;
Der Fehler ist in der Zeile:
if (customer = nil)
Möglicherweise ruft ein anderer Thread Folgendes auf:
customer := TCustomer.Create;
und der Variablen wird ein Wert zugewiesen, bevor die Konstruktion erfolgt. Dies bewirkt, dass der Thread zuannehmen Dascustomer
ist ein gültiges Objekt, nur weil die Variable zugewiesen ist.
Kann dieser Singleton-Fehler mit mehreren Threads in Delphi (5) auftreten?
Bonus-Frage
Gibt es eine akzeptierte, thread-safe,einmalige Initialisierung Entwurfsmuster für Delphi? Viele Leute haben umgesetztSingletons in Delphi durch ÜberschreibenNewInstance
undFreeInstance
; Ihre Implementierungen schlagen in mehreren Threads fehl.
Genau genommen bin ich nicht hinter einer Antwort zurück, wie und zu implementieren istSingleton, aberLazy-Initialisierung. WährendSingletons könnenbenutzen Lazy-Initialisierung,Lazy Initialisierung ist nicht auf Singletons beschränkt.
Aktualisieren
Zwei Leute schlugen eine Antwort vordas enthält einen häufigen Fehler. Dasfehlerhafter doppelt überprüfter Sperralgorithmus in Delphi übersetzt:
// Broken multithreaded version
// "Double-Checked Locking" idiom
if (customer = nil) then
begin
criticalSection.Enter;
try
if (customer = nil) then
customer := TCustomer.Create;
finally
criticalSection.Leave;
end;
end;
Result := customer;
VonWikipedia:
Intuitiv scheint dieser Algorithmus eine effiziente Lösung des Problems zu sein. Diese Technik weist jedoch viele subtile Probleme auf und sollte normalerweise vermieden werden.
Ein weiterer Vorschlag für einen Buggy:
function SacrificialCustomer: TCustomer;
var
tempCustomer: TCustomer;
begin
tempCustomer = customer;
if (tempCustomer = nil) then
begin
criticalSection.Enter;
try
if (customer = nil) then
begin
tempCustomer := TCustomer.Create;
customer := tempCustomer;
end;
finally
criticalSection.Leave;
end;
end;
Result := customer;
end;
Aktualisieren
Ich habe Code erstellt und mir das CPU-Fenster angesehen. Es scheint, dass dieser Compiler mit meinen Optimierungseinstellungen unter dieser Windows-Version mit diesem Objekt zuerst das Objekt erstellt.dann weist die Variable zu:
customer := TCustomer.Create;
mov dl,$01
mov eax,[$0059d704]
call TCustomer.Create
mov [customer],eax;
Result := customer;
mov eax,[customer];
Natürlich kann ich nicht sagen, dass das garantiert immer so funktioniert.