简体   繁体   English

C# 代表线程安全吗?

[英]Are C# delegates thread-safe?

If you have a class instance with a delegate member variable and multiple threads invoke that delegate (assume it points to a long-running method), is there any contention issues?如果您有一个带有委托成员变量的 class 实例并且多个线程调用该委托(假设它指向一个长时间运行的方法),是否存在任何争用问题?

Do you need to lock around the delegate or is it safe for each thread to call the method the delegate points to, since each thread gets it's own call stack?您是否需要锁定委托,或者每个线程调用委托指向的方法是否安全,因为每个线程都有自己的调用堆栈?

Regarding the invocation of the delegate the answer is yes.关于委托的调用,答案是肯定的。

Invoking a delegate is thread-safe because delegates are immutable.调用委托是线程安全的,因为委托是不可变的。 However, you must make sure that a delegate exists first.但是,您必须首先确保委托存在。 This check may require some synchronization mechanisms depending on the level of safety desired.此检查可能需要一些同步机制,具体取决于所需的安全级别。

For example, the following could throw a NullReferenceException if SomeDelegate were set to null by another thread between the null check and the invocation.例如,如果在 null 检查和调用之间的另一个线程将SomeDelegate设置为 null,则以下内容可能会引发NullReferenceException

if (SomeDelegate != null)
{    
  SomeDelegate();
}

The following is a little more safe.下面的安全一点。 Here we are exploiting the fact that delegates are immutable.在这里,我们利用了委托是不可变的这一事实。 Even if another thread modifies SomeDelegate the code is harded to prevent that pesky NullReferenceException .即使另一个线程修改了SomeDelegate ,代码也很难防止讨厌的NullReferenceException

Action local = SomeDelegate;
if (local != null)
{
  local();
}

However, this might result in the delegate never being executed if SomeDelegate was assigned a non-null value in another thread.但是,如果SomeDelegate在另一个线程中被分配了一个非空值,这可能会导致委托永远不会被执行。 This has to do with a subtle memory barrier problem.这与一个微妙的 memory 屏障问题有关。 The following is the safest method.以下是最安全的方法。

Action local = Interlocked.CompareExchange(ref SomeDelegate, null, null);
if (local != null)
{
  local();  
}

Regarding the execution of the method referenced by the delegate the answer is no.关于委托引用的方法的执行,答案是否定的。

You will have to provide your own thread-safety guarentees via the use of synchronization mechanisms.您必须通过使用同步机制来提供自己的线程安全保证。 This is because the CLR does not automatically provide thread-safety guarentees for the execution of delegates.这是因为 CLR 不会自动为委托的执行提供线程安全保证。 It might be the case that the method does not require any further synchronization to make it safe especially if it never access shared state.可能是该方法不需要任何进一步的同步以使其安全,特别是如果它从不访问共享的 state。 However, if the method reads or writes from a shared variable then you will have to consider how to guard against concurrent access from multiple threads.但是,如果该方法从共享变量读取或写入,那么您将不得不考虑如何防止来自多个线程的并发访问。

No they are not thread-safe and yes you'll have to manage concurrency yourself.不,它们不是线程安全的,是的,您必须自己管理并发。

Directly from the documentation of MulticastDelegate :直接来自MulticastDelegate的文档:

Any public static (Shared in Visual Basic) members of this type are thread safe.这种类型的任何公共 static(在 Visual Basic 中共享)成员都是线程安全的。 Any instance members are not guaranteed to be thread safe.不保证任何实例成员都是线程安全的。

The Delegate class contains the same information, so there you have it. 代表class 包含相同的信息,所以你有它。

Modifying an event is not thread-safe, but invoking a delegate is.修改事件不是线程安全的,但调用委托是。 Since a delegate is immutable, it is thread-safe.由于委托是不可变的,因此它是线程安全的。 See remarks here MSDN Delegate class :请参阅此处的备注MSDN 代表 class

Borrowed from here : In CLR Via C# Richter points out a few subtle points about event invocation in multi-threaded classes:这里借用:在 CLR 通过 C# Richter 指出了关于多线程类中事件调用的一些微妙点:

A delegate chain is immutable;委托链是不可变的; a new chain is created to replace the first.创建一个新链来替换第一个链。 A delegate chain with zero subscribers is null.零订阅者的委托链是 null。 That means (if your event is public) it may transition from null to non-null and vice versa, at any time.这意味着(如果您的事件是公开的)它可以随时从 null 转换为非空值,反之亦然。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM