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