Относительно вызова делегата ответ - да.
Вызов делегата является потокобезопасным, поскольку делегаты являются неизменяемыми. Однако вы должны убедиться, что делегат существует первым. Эта проверка может потребовать некоторых механизмов синхронизации в зависимости от желаемого уровня безопасности.
Например, следующее может выдать 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 не предоставляет автоматически гарантии безопасности потоков для выполнения делегатов. Может случиться так, что метод не требует дальнейшей синхронизации, чтобы сделать его безопасным, особенно если он никогда не обращается к общему состоянию. Однако, если метод читает или пишет из общей переменной, вам придется подумать, как защититься от одновременного доступа из нескольких потоков.