【发布时间】:2018-07-18 21:13:11
【问题描述】:
我正在寻找可能对此了解更多的人,我的直觉告诉我答案是“不,它不是线程安全的”,但我想确定。
为了说明我的问题,我提供了这个类的一些上下文
public class MyContext
{
private readonly object _lock = new object();
public delegate bool MyDelegate(MyContext context);
private MyDelegate _multicastDelegate;
public MyContext()
{
_multicastDelegate = null;
}
public void AddDelegate(MyDelegate del)
{
lock(_lock)
{
_multicastDelegate += del;
}
}
public void RemoveDelegate(MyDelegate del)
{
lock(_lock)
{
_multicastDelegate += del;
}
}
public void Go()
{
_multicastDelegate.Invoke(this);
}
}
编辑:我在上面的示例中添加了锁,但这真的不是我的问题的重点。
我试图更好地理解保存调用列表的数组是否是线程安全的。坦率地说,我并不清楚这一切是如何组合在一起的,我们将不胜感激。
根据我找到的文档,唯一没有提供真正见解的引用如下:
MulticastDelegate 有一个委托的链接列表,称为调用列表,由一个或多个元素组成。调用多播委托时,调用列表中的委托按照它们出现的顺序被同步调用。如果在执行列表期间发生错误,则会引发异常。
https://msdn.microsoft.com/en-us/library/system.multicastdelegate.aspx
提前致谢。
【问题讨论】:
-
您需要在此上下文中定义“线程安全”才能使问题有意义。请注意,使用
+=和-=不会修改现有的委托实例 - 它返回一个 new 实例。您显示的代码不是线程安全的,因为如果两个线程同时调用(比如说)AddDelegate,您很容易最终会发现其中一个被“错过”。这就是自动生成的类字段事件访问器做更多工作的原因。 -
我在整理代码示例时可能有点太匆忙了。鉴于您的评论,我的问题是关于添加/删除调用列表。
-
@JonSkeet -- 我做了一些编辑,但我认为我的措辞需要帮助......我假设调用列表本质上是一个数组。我想知道该数组是否可以线程安全地添加/删除项目。
-
委托对象是不可变的。从语法糖很难看出,但委托目标列表永远不会被修改。只有构造函数会创建该列表。因此,当您调用 Go() 而同时另一个线程忙于调用 Add 或 Remove 时,什么都不会发生。除了未调用添加的目标方法或仍调用已删除的目标。这通常是一个线程竞争错误,不值得“线程安全”这个绰号,但它不会让客户端程序员感到惊讶。嗯,不应该。
-
@HansPassant 我认为你在谈到 Jon 在他的评论中所做的同一个问题,那就是“线程安全”是一个相当没有意义的术语,因为它在不同的上下文中意味着不同的东西。
标签: c# multithreading thread-safety multicastdelegate