Подтвердить что ты не робот

Являются ли делегаты С# потокобезопасными?

Если у вас есть экземпляр класса с переменной-членом делегата, и несколько потоков вызывают это делегирование (предположим, что это указывает на длительный метод), есть ли какие-либо проблемы с обсуждением?

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

4b9b3361

Ответ 1

Что касается вызова делегата, то да.

Вызов делегата является потокобезопасным, потому что делегаты являются неизменными. Однако вы должны убедиться, что делегат существует первым. Эта проверка может потребовать некоторых механизмов синхронизации в зависимости от требуемого уровня безопасности.

Например, следующее может вызывать NullReferenceException, если SomeDelegate были установлены в нуль другим потоком между нулевой проверкой и вызовом.

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

Ниже приведено несколько более безопасное. Здесь мы используем тот факт, что делегаты неизменны. Даже если другой поток изменяет SomeDelegate, код не может предотвратить этот pesky NullReferenceException.

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

Однако это может привести к тому, что делегат никогда не будет выполнен, если SomeDelegate было присвоено ненулевое значение в другом потоке. Это связано с проблемой тонкой проблемы с памятью. Ниже приведен самый безопасный метод.

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

Что касается выполнения метода, на который ссылается делегат, то ответ отсутствует.

Вам необходимо будет предоставить свои собственные гарантии безопасности потоков с помощью механизмов синхронизации. Это связано с тем, что CLR автоматически не предоставляет гарантию безопасности потока для выполнения делегатов. Возможно, этот метод не требует никакой дополнительной синхронизации для обеспечения безопасности, особенно если он никогда не получает доступ к общему состоянию. Однако, если метод читает или записывает из общей переменной, вам придется подумать о том, как защитить от одновременного доступа из нескольких потоков.

Ответ 2

Нет, они не являются потокобезопасными, и да, вам придется управлять concurrency самостоятельно.

Ответ 3

Непосредственно из документации MulticastDelegate:

Любые публичные статические (Shared in Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не гарантируют безопасность потоков.

Класс Delegate содержит ту же информацию, что и у вас.

Ответ 4

Изменение события не является потокобезопасным, но вызывает делегат. Поскольку делегат является неизменным, он является потокобезопасным. См. Примечания здесь Класс делегата MSDN:

Заимствован из здесь: В CLR Via С# Richter указывает несколько тонких точек на вызов события в многопоточных классах:

Цепочка делегатов неизменна; новая цепочка создается для замены первой. Цепочка делегата с нулевыми подписчиками равна нулю. Это означает, что (если ваше событие общедоступно), он может в любой момент перейти от нулевого значения в ненулевой и наоборот.