¿Delphi asigna la variable antes de que se construya el objeto?
¿Delphi asigna una variable de instancia antes de que el objeto esté completamente construido?
En otras palabras, dada una variable:
var
customer: TCustomer = nil;
luego construimos un cliente y lo asignamos a la variable:
customer := TCustomer.Create;
Es posible quecustomer
puede no sernil
, pero no apuntan a una totalmente construidaTCustomer
?
Esto se convierte en un problema al realizar la inicialización perezosa:
function SacrifialCustomer: TCustomer;
begin
if (customer = nil) then
begin
criticalSection.Enter;
try
customer := TCustomer.Create;
finally
criticalSection.Leave;
end;
end;
Result := customer;
end;
El error está en la línea:
if (customer = nil)
Es posible que otro hilo llame:
customer := TCustomer.Create;
y a la variable se le asigna un valor antes de que ocurra la construcción.. Esto hace que el hiloasumir esecustomer
es un objeto válido simplemente porque la variable está asignada.
¿Puede suceder este error de singleton multihilo en Delphi (5)?
Pregunta extra
¿Hay una aceptada, segura de hilo,inicialización única patrón de diseño para Delphi? Muchas personas han implementadosingletons en Delfos anulandoNewInstance
yFreeInstance
; Sus implementaciones fallarán en múltiples hilos.
Hablando estrictamente, no busco una respuesta sobre cómo implementar ysemifallo, peroinicialización perezosa. Mientrassingletons puedeutilizar inicialización perezosa,inicialización perezosa no se limita a singletons.
Actualizar
Dos personas sugirieron una respuesta.que contiene un error común losalgoritmo de bloqueo de doble comprobación roto traducido a Delphi:
// 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;
DesdeWikipedia:
Intuitivamente, este algoritmo parece una solución eficiente al problema. Sin embargo, esta técnica tiene muchos problemas sutiles y, por lo general, debe evitarse.
Otra sugerencia de 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;
Actualizar
Creé algo de código y miré la ventana de la CPU. Parece que este compilador, con mi configuración de optimización, en esta versión de Windows, con este objeto, construye primero el objeto,entonces Asigna la variable:
customer := TCustomer.Create;
mov dl,$01
mov eax,[$0059d704]
call TCustomer.Create
mov [customer],eax;
Result := customer;
mov eax,[customer];
Por supuesto, no puedo decir que eso garantice que siempre funcione de esa manera.