本文介绍了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; 结束结束结果:= customer; 结束 错误在下列行中: if(customer = nil) 线程调用: customer:= TCustomer.Create; 并且在构造之前赋予变量值。这导致线程假定 客户是一个有效的对象,因为该变量被分配。 $ b $这个多线程单例错误可以在Delphi(5)中发生吗? 奖金问题 是否有接受,线程安全的一次初始化 Delphi的设计模式?许多人通过覆盖 NewInstance 和 FreeInstance ,在Delphi中实现了他们的实现将在多线程中失败。 严格来说,我不是在如何实现和单例的答案之后,而是懒惰初始化。 单身人士可以使用懒惰初始化,延迟初始化不限于单身人士。 更新 两个人建议一个答案,其中包含一个常见的错误。将断开的双重锁定算法转换为Delphi : //破碎的多线程 //双重锁定成语如果(customer = nil)然后 begin criticalSection.Enter; try if(customer = nil)then customer:= TCustomer.Create; finally criticalSection.Leave; 结束结束结果:= 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; 结束 finally criticalSection.Leave; 结束结束结果:= customer; 结束 更新 我创建了一些代码,并查看了cpu窗口。似乎这个编译器,在我的优化设置下,在这个版本的Windows上,用这个对象,首先构造对象,然后分配变量: customer:= TCustomer.Create; mov dl,$ 01 mov eax,[$ 0059d704] 调用TCustomer.Create mov [customer],eax; 结果:= customer; mov eax,[customer]; 当然我不能说保证总是这样工作。解决方案我的阅读你的问题是你问这个问题: 我如何使用Delphi 5定位x86硬件,实现单线程的线程安全的延迟初始化。 知识你有三个选择。 1。使用锁 函数GetCustomer:TCustomer; begin Lock.Acquire; 尝试如果没有分配(客户)然后//客户是一个全局变量客户:= TCustomer.Create; 结果:=客户; finally Lock.Release; 结束结束 这样做的缺点是如果在 GetCustomer 然后锁定的序列化将禁止缩放。我怀疑人们担心的不仅仅是必要的。例如,如果您有一个执行大量工作的线程,该线程可以将单引号的本地副本作为参考,以减少争用。 procedure ThreadProc; var MyCustomer:TCustomer; begin MyCustomer:= GetCustomer; //用MyCustomer 结束做很多工作 2。双重锁定 这是一种技术,一旦创建单例,就可以避免锁争用。 函数GetCustomer:TCustomer; 开始如果分配(客户)然后开始结果:=客户; 退出; 结束 Lock.Acquire; 尝试如果没有分配(客户)然后客户:= TCustomer.Create; 结果:=客户; finally Lock.Release; 结束结束 双重锁定是一种具有相当方格的历史的技术。最着名的讨论是双重检查的锁定被破坏声明。这主要是在Java的上下文中,所描述的问题不适用于您的情况(Delphi编译器,x86硬件)。事实上,对于Java,随着JDK5的到来,我们现在可以说双重锁定是固定的。 Delphi编译器不会重新命令写入关于对象的构造的单例变量。此外,强大的x86内存模式意味着处理器重新排序不会破坏这一点。请参阅谁在x86上订购记忆栅栏? 简单来说,双重锁定在Delphi x86上没有损坏。此外,x64内存模式也很强大,双重锁定也不会被破坏。 3。比较和交换 如果您不介意创建单个类的多个实例,然后丢弃除一个以外的所有实例,您可以使用比较和交换。 VCL的最新版本使用了这种技术。看起来像这样: 函数GetCustomer; var LCustomer:TCustomer; begin 如果没有分配(客户)然后 begin LCustomer:= TCustomer.Create; 如果InterlockedCompareExchangePointer(指针(客户),LCustomer,nil)<>零,然后 LCustomer.Free; 结束结果:=客户; 结束 Does Delphi assign an instance variable before the object is fully constructed?In other words, given a variable: var customer: TCustomer = nil; we then construct a customer and assign it to the variable:customer := TCustomer.Create;Is it possible that customer can be not nil, but not point to a fully constructed TCustomer?This becomes a problem when performing lazy initialization:function SacrifialCustomer: TCustomer;begin if (customer = nil) then begin criticalSection.Enter; try customer := TCustomer.Create; finally criticalSection.Leave; end; end; Result := customer;end;The bug is in the line:if (customer = nil) It is possible that another thread calls:customer := TCustomer.Create;and the variable is assigned a value before construction happens. This causes the thread to assume that customer is a valid object simply because the variable is assigned.Can this multi-threaded singleton bug happen in Delphi (5)?Bonus QuestionIs there an accepted, thread-safe, one-time initialization design pattern for Delphi? Many people have implemented singletons in Delphi by overriding NewInstance and FreeInstance; their implementations will fail in multiple threads.Strictly speaking i'm not after an answer on how to implement and singleton, but lazy-initialization. While singletons can use lazy-initialization, lazy initialization is not limited to singletons.UpdateTwo people suggested an answer that contains a common mistake. The broken double-checked locking algorithm translated to Delphi:// Broken multithreaded version// "Double-Checked Locking" idiomif (customer = nil) thenbegin criticalSection.Enter; try if (customer = nil) then customer := TCustomer.Create; finally criticalSection.Leave; end;end;Result := customer;From Wikipedia: Intuitively, this algorithm seems like an efficient solution to the problem. However, this technique has many subtle problems and should usually be avoided.Another buggy suggestion: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;Updatei created some code and looked at the cpu window. It seems that this compiler, with my optimization settings, on this version of Windows, with this object, constructs the object first, then assigns the variable:customer := TCustomer.Create; mov dl,$01 mov eax,[$0059d704] call TCustomer.Create mov [customer],eax;Result := customer; mov eax,[customer];Of course i cannot say that's guaranteed to always work that way. 解决方案 My reading of your question is that you are asking this: How can I, using Delphi 5 targeting x86 hardware, implement thread-safe lazy initialization of a singleton.To the best of my knowledge you have three options.1. Use a lockfunction GetCustomer: TCustomer;begin Lock.Acquire; try if not Assigned(Customer) then // Customer is a global variable Customer := TCustomer.Create; Result := Customer; finally Lock.Release; end;end;The downside of this is that if there is contention on GetCustomer then the serialization of the lock will inhibit scaling. I suspect that people worry about that a lot more than is necessary. For example, if you have a thread that performs a lot of work, that thread can take a local copy of the reference to the singleton to reduce the contention.procedure ThreadProc;var MyCustomer: TCustomer;begin MyCustomer := GetCustomer; // do lots of work with MyCustomerend;2. Double checked lockingThis is a technique that allows you, once the singleton has been created, to avoid the lock contention.function GetCustomer: TCustomer;begin if Assigned(Customer) then begin Result := Customer; exit; end; Lock.Acquire; try if not Assigned(Customer) then Customer := TCustomer.Create; Result := Customer; finally Lock.Release; end;end;Double checked locking is a technique with a rather chequered history. The most famous discussion is The "Double-Checked Locking is Broken" Declaration. This is set mostly in the context of Java and the problems described do not apply to your situation (Delphi compiler, x86 hardware). Indeed, for Java, with the advent of JDK5, we can now say that Double-Checked Locking is Fixed.The Delphi compiler doesn't re-order the write to the singleton variable with respect to the construction of the object. What's more, the strong x86 memory model means that processor re-orderings don't break this. See Who ordered memory fences on an x86?Simply put, double checked locking is not broken on Delphi x86. What's more, the x64 memory model is also strong and double checked locking is not broken there either.3. Compare and swapIf you don't mind the possibility of creating multiple instances of the singleton class, and then discarding all but one, you can use compare and swap. Recent versions of the VCL make use of this technique. It looks like this:function GetCustomer;var LCustomer: TCustomer;begin if not Assigned(Customer) then begin LCustomer := TCustomer.Create; if InterlockedCompareExchangePointer(Pointer(Customer), LCustomer, nil) <> nil then LCustomer.Free; end; Result := Customer;end; 这篇关于Delphi在构造对象之前分配变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 09-27 00:45