我正在寻找可能对此有更多了解的人,我的直觉告诉我答案是“不,这不是线程安全的”,但我想确定。
为了说明我的问题,我已经提供了这个类的一些上下文
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
提前致谢。
答案 0 :(得分:4)
Delegates immutable 。你永远不会改变一个代表。任何看似改变委托的方法实际上都是在创建一个新实例。
代表是不可改变的;一旦创建,代理的调用列表就不会改变。
因此,在调用委托时,可能无法更新调用列表。
但是,您必须防范并且未能在您的方法中执行的操作是委托实际上可能是null
。
(new MyContext()).Go();
会导致异常。您曾经不得不通过将值读入局部变量,测试它为null然后使用它来调用它来防范这种情况。它现在可以更容易地解决为:
public void Go()
{
_multicastDelegate?.Invoke(this);
}
答案 1 :(得分:-1)
MSDN文档使用的线程安全定义是指属性同步的代码。它通常不会对它同步的内容进行说明,但它可以是静态成员的类型,也可以是实例成员的实例本身,或者它可以是一些内部对象,例如许多集合类型中的SyncRoot
。
虽然委托是不可变的,但您仍必须正确同步。与Java不同,.NET和C#不保证安全发布,因此如果您不保证同步,则可以在其他线程 1 中观察部分初始化的对象。
要使代码线程安全,您只需在从委托字段中读取时使用_lock
,但您可以在锁外调用Invoke
,登陆代理人保持自己的线程安全。
public class MyContext
{
private readonly object _lock = new object();
public delegate bool MyDelegate(MyContext context);
private MyDelegate _delegate;
public MyContext()
{
}
public void AddDelegate(MyDelegate del)
{
lock (_lock)
{
_delegate += del;
}
}
public void RemoveDelegate(MyDelegate del)
{
lock (_lock)
{
// You had a bug here, +=
_delegate -= del;
}
}
public void Go()
{
MyDelegate currentDelegate;
lock (_lock)
{
currentDelegate = _delegate;
}
currentDelegate?.Invoke(this);
}
}