Присваивает ли Delphi переменную до создания объекта?

Присваивает ли Delphi переменную экземпляра до того, как объект будет полностью построен?

Другими словами, учитывая переменную:

var
   customer: TCustomer = nil; 

мы затем создаем клиента и присваиваем его переменной:

customer := TCustomer.Create;

Возможно ли, что клиентне может быть nil, но не указывает на полностью построенный TCustomer?


Это становится проблемой при выполнении ленивой инициализации:

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

Ошибка находится в строке:

if (customer = nil) 

Возможно, другой поток вызывает:

customer := TCustomer.Create;

и переменной присваивается значение до того, как происходит построение. Это заставляет поток предположить, что клиентявляется допустимым объектом просто потому, что переменная назначена.

Может ли эта многопоточная одноэлементная ошибка возникать в Delphi (5)?


Дополнительный вопрос

Существует ли общепринятый, потокобезопасный шаблон проектирования однократной инициализациидля Delphi? Многие реализовали синглтоныв Delphi, переопределив NewInstanceи FreeInstance; их реализации потерпят неудачу в нескольких потоках.

Строго говоря, мне нужен не ответ о том, как реализовать и singleton, а lazy-initialization. В то время как синглтонымогут использоватьленивую инициализацию, ленивая инициализацияне ограничивается одиночками.

Обновление

Два человека предложили ответ , который содержит распространенную ошибку. сломанный алгоритм блокировки с двойной проверкой, переведенный в 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;

Из Википедии:

Интуитивно этот алгоритм кажется эффективным решением проблемы. Однако у этого метода есть много тонких проблем, и его обычно следует избегать.


Еще одно предложение по ошибкам:

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;

Обновление

Я создал некоторый код и посмотрел на окно процессора. Кажется, что этот компилятор с моими настройками оптимизации в этой версии Windows с этим объектом сначала создает объект, , затем присваивает переменную:

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

Конечно, я не могу сказать, что это гарантированно всегда работает сюда.

5
задан Ian Boyd 29 May 2012 в 21:08
поделиться