问题描述
|
如果您有一个带有委托成员变量的类实例,并且有多个线程调用该委托(假设它指向一个长期运行的方法),那么是否存在争用问题?
您是否需要锁定委托,还是每个线程都可以安全地调用委托所指向的方法,因为每个线程都有自己的调用堆栈?
解决方法
关于委托的调用,答案是肯定的。
调用委托是线程安全的,因为委托是不可变的。但是,您必须确保首先存在一个委托。根据所需的安全级别,此检查可能需要一些同步机制。
例如,如果在空值检查和调用之间的另一个线程将
SomeDelegate
设置为null,则以下内容可能会抛出ѭ0。
if (SomeDelegate != null)
{
SomeDelegate();
}
以下内容更加安全。在这里,我们正在利用委托是不可变的这一事实。即使另一个线程修改了“ 1”,也很难阻止该讨厌的“ 0”。
Action local = SomeDelegate;
if (local != null)
{
local();
}
但是,如果在另一个线程中为SomeDelegate
分配了非空值,则可能永远不会执行委托。这与一个微妙的内存屏障问题有关。以下是最安全的方法。
Action local = Interlocked.CompareExchange(ref SomeDelegate,null,null);
if (local != null)
{
local();
}
关于委托人引用的方法的执行,答案是否定的。
您将必须通过使用同步机制来提供自己的线程安全保证人。这是因为CLR不会自动为委托的执行提供线程安全保证。可能的情况是,该方法不需要任何进一步的同步即可使其安全,尤其是在永不访问共享状态的情况下。但是,如果该方法从共享变量读取或写入,则必须考虑如何防止来自多个线程的并发访问。
, 不,它们不是线程安全的,是的,您必须自己管理并发性。
, 直接从MulticastDelegate的文档中:
此类型的任何公共static(在Visual Basic中为Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。
Delegate类包含相同的信息,因此就可以了。
, 修改事件不是线程安全的,但是调用委托是安全的。
由于委托是不可变的,因此它是线程安全的。请参阅此处的MSDN Delegate类备注:
从这里借来的:
在CLR Via C#中,Richter指出了有关多线程类中事件调用的一些细微之处:
委托链是不可变的。将创建一个新链来替换第一个。
零订阅者的委托链为空。
这意味着(如果您的活动是公开的)它可以随时从null转换为non-null,反之亦然。