События C# и потокобезопасность



обновление



как на C# 6, ответ на этот вопрос:



SomeEvent?.Invoke(this, e);




я часто слышу / читаю следующие советы:



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



// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;

if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list


Обновлено: я думал, читая об оптимизации, что это также может потребовать, чтобы член события был изменчивым, но Джон Скит заявляет в своем ответе, что CLR не оптимизирует копию.



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



// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...


фактическая последовательность может быть такая смесь:



// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;

// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...

if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list


дело в том, что OnTheEvent работает после того, как автор отписался, и все же они просто отписались специально, чтобы этого не произошло. Конечно, что действительно необходимо, это реализация пользовательского события с соответствующей синхронизацией в add и remove аксессоры. И кроме того, существует проблема возможных тупиков, если блокировка удерживается во время запуска события.



это Культ Карго Программирования? Похоже, что так-многие люди должны принимать этот шаг, чтобы защитить их код из нескольких потоков, когда на самом деле мне кажется, что события требуют гораздо больше внимания, чем это, прежде чем они могут быть использованы в рамках многопоточной конструкции. Следовательно, люди, которые не принимают эту дополнительную заботу, могут также игнорировать этот совет - это просто не проблема для однопоточных программ, и на самом деле, учитывая отсутствие volatile в большинстве онлайн-кода примера, совет может не иметь никакого эффекта вообще.



(и не намного ли проще просто назначить пустое delegate { } на объявление члена, так что вам никогда не нужно проверять null в первую очередь?)



обновление: в случае, если это не было ясно, я понял намерение совета - избегать исключения нулевой ссылки при любых обстоятельствах. Я хочу сказать, что это конкретное исключение null reference может произойти только в том случае, если другой поток удаляется из события, и единственная причина для этого заключается в том, чтобы гарантировать, что никакие дальнейшие вызовы не будут получены через то событие, которое явно не достигается этой техникой. Вы бы скрывали состояние гонки - было бы лучше раскрыть его! Это нулевое исключение помогает обнаружить злоупотребление вашим компонентом. Если вы хотите, чтобы ваш компонент был защищен от злоупотреблений, вы можете последовать примеру WPF - сохранить идентификатор потока в своем конструкторе, а затем создать исключение, если другой поток пытается напрямую взаимодействовать с вашим компонентом. Или же реализовать действительно потокобезопасный компонент (не простой задача.)



поэтому я утверждаю, что просто делать эту идиому копирования/проверки-это программирование культа груза, добавляя беспорядок и шум в ваш код. На самом деле для защиты от других потоков требуется гораздо больше работы.



обновление в ответ на сообщения в блоге Эрика Липперта это:



Итак, есть важная вещь, которую я пропустил о обработчиках событий: "обработчики событий должны быть надежными перед лицом вызова даже после того, как событие было отписано", и очевидно, поэтому нам нужно только заботиться о возможности делегирования события null. это требование к обработчикам событий документировано в любом месте?



и так: "есть и другие способы решить эту проблему; например, инициализация обработчика пустое действие, которое не удаляется. Но выполнение нулевой проверки является стандартным шаблоном."



Итак, один оставшийся фрагмент моего вопроса, почему явный-null-проверьте "стандартная модель"? альтернатива, назначающая пустой делегат, требует только = delegate {} чтобы добавить к объявлению события, и это устраняет эти маленькие груды вонючей церемонии из каждого места, где происходит событие. Было бы легко убедиться, что пустой делегат дешев для создания экземпляра. Или я все еще что-то упускаю?



конечно, это должно быть так (как предложил Джон Скит), это просто .NET 1.X совет, который не вымер, как это должно было сделать в 2005?

614   0  

Comments

    Ничего не найдено.