Стандартный способ обработки ошибок, возникающих из потоков - PullRequest
1 голос
/ 20 августа 2010

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

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

В этот момент вы, вероятно, задаетесь вопросом: «Зачем ему создавать поток, а затем вызывать Invoke? Почему бы просто не вызвать функцию?». Проблема в том, что я хочу предоставить вызывающей стороне гибкость с помощью параметра, который определяет, должна ли операция быть синхронной или асинхронной.

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

Чтобы убедиться, что у меня есть общее представление о поведении исключений в потоках, а также о том, что опровергает утверждение в этом вопросе о SO (по крайней мере, до .NET 3.5), я написал тестовый код в приложении WPF:

код удален

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

При условии, что вы согласны с моими тестами, приведенными выше, мой вопрос заключается просто в следующем: является ли приемлемой практика писать мой метод, который предлагает как синхронизацию, так и асинхронное поведение, когда вся бизнес-логика заключена в блок try / catch? Внутренние ошибки попадают в ловушку внутри потока, и если это синхронизирующий вызов, просто сгенерируйте исключение, а если это асинхронный вызов, просто вызовите событие? В приведенном выше примере кода я также выбрасываю исключение после вызова события. Я не уверен, если или почему это вызовет у меня какие-либо проблемы.

Pseudo-pseudocode:

TestFunc(async);

private TestFunc(bool async)
{
  try {
    throw new MyAppException("error occurred.");
  } catch(MyAppException ex) {
    async ? RaiseErrorEvent : throw;
  }
}

UPDATE

Хорошо, как сказал Ганс, это вызовет проблемы, независимо от того, что если я сгенерирую исключение из потока - точка. Я смог проверить этот случай и, конечно же, исключение выдается, и если вы нажмете F5, оно просто будет выдано снова и снова и никогда не завершит поток. Я не понимаю этого конкретного поведения - это просто, как он работает при запуске через отладчик? Я предполагаю, что это означает, что я должен проверить, называется ли метод синхронизированным или асинхронным. Если синхронизировать, выкинь ошибку. Если асинхронно, поднять событие.

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

Ответы [ 2 ]

2 голосов
/ 20 августа 2010

Не уверен, что вы пытаетесь доказать с помощью этого кода.Просто напишите обработчик исключений для AppDomain.Current.UnhandledException и зарегистрируйте или отобразите значение e.ExceptionObject.ToString ().

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

Обработка события «бомбардировка» сложна сама по себе.Код клиента не может даже отобразить окно сообщения, оно легко исчезнет за вашим главным окном.Прямое обновление пользовательского интерфейса не допускается.Вызов должен быть направлен в поток пользовательского интерфейса.Лучший способ - оставить на усмотрение клиентского кода решение о том, как он хочет получать уведомления.Свойство FileSystemWatcher.SynchronizingObject является хорошим шаблоном.

1 голос
/ 20 августа 2010

Если вы говорите о выполнении задачи асинхронно, наиболее типичным шаблоном является перехват исключения в потоке и его повторное генерирование после завершения синхронизации для ожидания завершения задачи.Например, шаблон IAsyncResult имеет структуру вызовов BeginX (), EndX (), в которой вы вызываете EndX () для блокировки до завершения.Вызов EndX () должен вызвать любое исключение, которое произошло в асинхронной операции, чтобы вызывающий мог решить, как его обработать.

Если вы используете .NET 4.0, параллельная библиотека задач имеет обширную поддержку для захвата и даже агрегации исключений при выполнении асинхронных задач, так что клиент, запустивший задачи, может решить, как их обрабатывать.

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