O Delphi atribui a variável antes que o objeto seja construído?

O Delphi atribui uma variável de instância antes que o objeto seja totalmente construído?

Em outras palavras, dada uma variável:

var
   customer: TCustomer = nil; 

Em seguida, construímos um cliente e o atribuímos à variável:

customer := TCustomer.Create;

É possível quecustomer pode não sernil, mas não aponta para um totalmente construídoTCustomer?

Isso se torna um problema ao executar a inicialização lenta:

function SacrifialCustomer: TCustomer;
begin
   if (customer = nil) then
   begin
      criticalSection.Enter;
      try
         customer := TCustomer.Create;
      finally 
         criticalSection.Leave;
      end;
   end;
   Result := customer;
end;

O bug está na linha:

if (customer = nil) 

É possível que outro encadeamento chame:

customer := TCustomer.Create;

e a variável recebe um valor antes que a construção aconteça. Isso faz com que o encadeamentoassumir naquelacustomer é um objeto válido simplesmente porque a variável é atribuída.

Esse bug singleton multi-thread pode acontecer em Delphi (5)?

Pergunta bônus

Existe um aceito, thread-safe,inicialização única padrão de design para Delphi? Muitas pessoas implementaramsingletons em Delphi, substituindoNewInstance eFreeInstance; suas implementações falharão em vários segmentos.

Estritamente falando eu não estou depois de uma resposta sobre como implementar esingleton, masinicialização lenta. Enquantosingletons possousar inicialização lenta,inicialização lenta não está limitado a singletons.

Atualizar

Duas pessoas sugeriram uma respostaque contém um erro comum. oalgoritmo de travamento duplo verificado traduzido para 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;

DeWikipedia:

Intuitivamente, esse algoritmo parece ser uma solução eficiente para o problema. No entanto, esta técnica tem muitos problemas sutis e geralmente deve ser evitada.

Outra sugestão 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;

Atualizar

Eu criei algum código e olhei para a janela da cpu. Parece que este compilador, com minhas configurações de otimização, nesta versão do Windows, com este objeto, constrói o objeto primeiro,então atribui a variável:

customer := TCustomer.Create;
       mov dl,$01
       mov eax,[$0059d704]
       call TCustomer.Create
       mov [customer],eax;
Result := customer;
       mov eax,[customer];

É claro que não posso dizer que é garantido que sempre funcione dessa maneira.

questionAnswers(4)

yourAnswerToTheQuestion