オブジェクトが完全に構築される前に、Delphi はインスタンス変数を割り当てますか?
つまり、変数が与えられた場合:
var
customer: TCustomer = nil;
次に、顧客を作成して変数に割り当てます。
customer := TCustomer.Create;
ではなく、完全に構築された を指していcustomer
ない可能性はありますか?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;
変数には、構築が行われる前に値が割り当てられます。これにより、変数が割り当てられているという理由だけで、スレッドはそれが有効なオブジェクトであると想定します。customer
このマルチスレッド シングルトンのバグは、Delphi (5) で発生する可能性がありますか?
ボーナス質問
Delphiの受け入れられた、スレッドセーフな、1 回限りの初期化設計パターンはありますか? 多くの人が、およびをオーバーライドして Delphi にシングルトンを実装しています。それらの実装は複数のスレッドで失敗します。NewInstance
FreeInstance
厳密に言えば、実装方法とsingletonについての回答は求めていませんが、lazy-initializationです。シングルトンは遅延初期化を使用できますが、遅延初期化はシングルトンに限定されません。
アップデート
よくある間違いを含む回答を 2 人が提案しました。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;
アップデート
私はいくつかのコードを作成し、CPU ウィンドウを見ました。このコンパイラは、私の最適化設定を使用して、このバージョンの Windows でこのオブジェクトを使用して、最初にオブジェクトを構築し、次に変数を割り当てているようです。
customer := TCustomer.Create;
mov dl,$01
mov eax,[$0059d704]
call TCustomer.Create
mov [customer],eax;
Result := customer;
mov eax,[customer];
もちろん、それが常にそのように機能することが保証されているとは言えません。