【发布时间】:2012-06-04 01:37:10
【问题描述】:
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;
bug就在一行:
if (customer = nil)
有可能是另一个线程调用:
customer := TCustomer.Create;
and the variable is assigned a value before construction happens。这会导致线程假设 customer 是一个有效的对象,因为变量被赋值了。
这种多线程单例的bug会在Delphi(5)中发生吗?
奖金问题
Delphi 是否有公认的、线程安全的one-time initialization 设计模式?许多人通过覆盖NewInstance 和FreeInstance 在Delphi 中实现了singletons;它们的实现将在多个线程中失败。
严格来说,我不是在回答如何实现和单例,而是延迟初始化。虽然单例可以使用延迟初始化,但延迟初始化不限于单例。
更新
两个人建议了一个答案that contains a common mistake. The broken double-checked locking algorithm translated to 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;
来自Wikipedia:
直观地说,这个算法似乎是解决问题的有效方法。但是,这种技术有许多微妙的问题,通常应该避免。
另一个错误的建议:
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];
当然,我不能说一定会一直这样工作。
【问题讨论】:
-
stackoverflow.com/questions/4475080/how-should-double-checked-locking-be-implemented-in-delphi 我认为这个问题是关于 delphi 中的双重检查锁定习语而不是关于变量初始化。
-
@BorisTreukhov 接受的代码可能会遇到同样的问题,具体取决于编译器的行为方式。
-
是的,确实:-(。stackoverflow.com/questions/232075/…
-
@IanBoyd - 我已将您对 buggy 临时变量建议的实现更改为我的想法(希望您不介意) .最有可能的是,这仍然存在问题,但正如我在 cmets 中提到的那样,我没有掌握它们。
-
@Lieven 好吧,我已经戒烟了,因为我太上瘾了。但是,由于这些平台上的内存模型,
hatchet实现的双重检查锁定在 x86 和 x64 上的 Windows 上所有已知的 Delphi 编译器上都是安全的。我希望它在 x86 上的 Mac OS 上也是安全的,但不知道。
标签: delphi lazy-loading delphi-5