`System.MulticastDelegate`是线程安全的吗?

时间:2018-02-08 10:24:25

标签: c# multithreading thread-safety multicastdelegate

我正在寻找可能对此有更多了解的人,我的直觉告诉我答案是“不,这不是线程安全的”,但我想确定。

为了说明我的问题,我已经提供了这个类的一些上下文

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

提前致谢。

2 个答案:

答案 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);
    }
}
  1. Microsoft的.NET Framework实现总是进行易失性写入(或者他们说),这种方式可以隐式地为您提供安全的发布,但我个人并不依赖于此。