Является ли `System.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 ответа

Решение

Делегаты неизменны. Вы никогда не меняете делегата. Любой метод, который, по-видимому, изменяет делегат, фактически создает новый экземпляр.

Делегаты неизменны; После создания список вызовов делегата не изменяется.

Таким образом, нет оснований для беспокойства о том, что список вызовов может быть обновлен во время вызова делегата.

Однако, что вы должны остерегаться, и чего вы не смогли сделать в вашем методе, так это когда делегат может быть null,

(new MyContext()).Go();

вызовет исключение. Раньше вы должны были защититься от этого, считывая значение в локальную переменную, проверяя его на null и затем вызывая его. Теперь это может быть легко решено как:

public void Go()
{
    _multicastDelegate?.Invoke(this);
}

Определение потоковой безопасности, используемое в документации MSDN, означает код, свойство которого синхронизировано. Обычно в нем не указывается, что именно синхронизируется, но это может быть тип для статических членов и сам экземпляр для членов экземпляра, или это может быть некоторый внутренний объект, такой как SyncRoot во многих типах коллекций.

Хотя делегаты являются неизменяемыми, вы все равно должны синхронизироваться правильно. .NET и C#, в отличие от Java, не гарантируют безопасную публикацию, поэтому, если вы не гарантируете синхронизацию, вы можете наблюдать частично инициализированный объект в других потоках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 всегда делает нестабильные записи (или, как они говорят), что неявно дает вам безопасную публикацию, но я лично не полагаюсь на это.
Другие вопросы по тегам