Являются ли делегаты C # поточно-ориентированными? - PullRequest
23 голосов
/ 14 июня 2011

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

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

Ответы [ 4 ]

23 голосов
/ 15 июня 2011

Относительно вызова делегата ответ - да.

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

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

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

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

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

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

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

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

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

7 голосов
/ 14 июня 2011

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

6 голосов
/ 14 июня 2011

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

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

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

2 голосов
/ 14 июня 2011

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

Заимствовано из здесь : В CLR через C # Рихтер указывает несколько тонких моментов, связанных с вызовом событий в многопоточных классах:1007 *

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...