Так как любой из обработчиков может выдать ошибку, и это помешает вызову остальных из них,
Вы говорите, что это плохо. Это очень хорошая вещь .Когда создается необработанное, неожиданное исключение, это означает, что весь процесс теперь находится в неизвестном, непредсказуемом, возможно, опасно нестабильном состоянии.
Выполнение большего количества кода на этом этапе может ухудшить ситуацию, а не улучшить ее.Самое безопасное, что можно сделать, когда это происходит, - это обнаружить ситуацию и вызвать аварийный вызов, который завершает весь процесс, не запуская больше кода.Вы не знаете, что будет делать ужасный код с большим количеством кода на этом этапе.
Я хочу игнорировать любые ошибки, возникающие в каждом отдельном обработчике.
Это супер опасная идея.Эти исключения говорят вам, что происходит что-то ужасное, а вы их игнорируете.
Другими словами, я не хочу, чтобы ошибка в одном обработчике нарушала выполнение других обработчиков в списке вызовов, поскольку ни эти другие обработчики, ни издатель событий не имеют никакого контроля над какими-либо конкретнымиКод обработчика событий.
Кто здесь главный? Кто-то добавляет эти обработчики событий к этому событию. То, что - это код, который отвечает за обеспечение правильной работы обработчиков событий в случае возникновения исключительной ситуации.
Затем я создаю объект блокировки и блокирую его в методах добавления и удаления открытого события, которые изменяют частную переменную-делегат с именем "event_handlers".
Конечно, все в порядке.Я подвергаю сомнению необходимость этой функции - у меня очень редко бывает ситуация, когда несколько потоков добавляют обработчики событий к событию, - но я позволю вам сказать, что вы находитесь в этой ситуации.
Нов этом сценарии этот код очень, очень, очень опасен:
lock (collection_lock)
foreach (Delegate handler in event_delegate.GetInvocationList())
try {handler.DynamicInvoke( args );}catch{}
Давайте подумаем, что там не так.
Нить Альфа входит в блокировку коллекции.
Предположим, есть еще один ресурс, foo, который также контролируется другой блокировкой.Thread Beta входит в блокировку foo для получения необходимых ему данных.
Затем Beta-поток принимает эти данные и пытается войти в блокировку коллекции, поскольку он хочет использовать содержимое foo в обработчике событий.
Тема бета теперь ожидает потока Альфа.Альфа-поток теперь вызывает делегата, который решает, что он хочет получить доступ к foo.Итак, он ждет бета-потока, и теперь у нас тупик.
Но разве мы не можем избежать этого, заказав замки? Нет, поскольку сама предпосылка вашего сценария заключается в том, что вы не знаете, что делают обработчики событий! Если вы уже знаете, что обработчики событий хорошо себя ведут в отношении порядка их блокировки, то вы можетепредположительно также известно, что они ведут себя хорошо, не вызывая исключений, и вся проблема исчезает.
ОК, поэтому давайте предположим, что вы делаете это вместо этого:
Delegate copy;
lock (collection_lock)
copy = event_delegate;
foreach (Delegate handler in copy.GetInvocationList())
try {handler.DynamicInvoke( args );}catch{}
Делегатынеизменяемый и копируется атомарно по ссылке, так что теперь вы знаете, что вы будете вызывать содержимое event_delegate, но не будете удерживать блокировку во время вызова.Это помогает?
Не совсем.Вы обменяли одну проблему на другую:
Поток Альфа берет блокировку, делает копию списка делегатов и покидает блокировку.
Поток Бета-версия берет блокировку, удаляет обработчик событияX из списка и уничтожает состояние, необходимое для предотвращения взаимоблокировки X.
Thread Alpha вступает во владение снова и запускает X из копии.Потому что бета просто уничтожил состояние, необходимое для правильного выполнения X, X взаимоблокировок.И снова вы зашли в тупик.
Обработчики событий обязаны этого не делать; они обязаны быть крепкими перед лицом их внезапно становящихся "несвежими". Похоже, вы находитесь в сценарии, когда вы не можете доверять обработчикам событий, чтобы они были хорошо написаны. Это ужасная ситуация; тогда вы не можете доверять любому коду, чтобы быть надежным в этом процессе. Вы, кажется, думаете, что существует какой-то уровень изоляции, который вы можете наложить на обработчик событий, перехватывая все его ошибки и путаясь, но это не так. Обработчики событий - это просто код, и они могут влиять на произвольное глобальное состояние в программе, как и любой другой код.
Короче говоря, ваше решение является универсальным, но оно не поточно-ориентированное и не безошибочное. Скорее, это усугубляет проблемы с потоками, такие как взаимоблокировки, и отключает системы безопасности.
Вы просто не можете отказаться от ответственности за правильность обработчиков событий, поэтому не пытайтесь. Напишите свои обработчики событий так, чтобы они были правильными - чтобы они правильно упорядочивали блокировки и никогда не создавали необработанные исключения ,
Если они неверны и в итоге выдают исключения, немедленно завершает процесс . Не пытайтесь запутаться, пытаясь запустить код, который сейчас работает в нестабильном процессе.
Судя по вашим комментариям к другим ответам, похоже, что вы думаете, что сможете принимать конфеты от незнакомцев без каких-либо вредных последствий. Вы не можете, не без намного большей изоляции. Вы не можете просто подписать случайный код на события в вашем процессе и надеяться на лучшее. Если у вас есть вещи, которые ненадежны из-за того, что вы запускаете сторонний код в своем приложении, вам нужна какая-то инфраструктура управляемых надстроек для обеспечения изоляции. Попробуйте найти MEF или MAF.