Опубликован: 15.10.2009 | Доступ: свободный | Студентов: 896 / 248 | Оценка: 4.42 / 4.20 | Длительность: 08:22:00
Специальности: Программист
Лекция 8:

Обработка исключений при использовании PFX

< Лекция 7 || Лекция 8: 12 || Лекция 9 >
Аннотация: В лекции рассматриваются вопросы обработки исключительных ситуаций связанных с выполнением в параллельных приложениях.

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

Основные принципы работы с исключительными ситуациями при использовании библиотеки PFX состоят в следующем:

  1. при возникновении исключения в задаче (task), как созданной явно, так и порожденной неявно, например, оператором Parallel.For, это исключение обрабатывается средствами самой библиотеки (если, конечно, перехват этого исключения не был предусмотрен самим программистом) и перенаправляется в ту задачу, которая ожидает завершения данной;
  2. при одновременном возникновении нескольких исключительных ситуаций (например, в разных параллельных ветках оператора Parallel.Invoke ), все они собираются в единое исключение типа System.Threading.AggregateException, которые переправляются дальше по цепочке вызовов задач;
  3. если возникла в точности одна исключительная ситуация, то на ее основе будет создан объект класса AggregateException в целях единообразной обработки всех исключительных ситуаций.

Исключительные ситуации типа AggregateException могут возникать при работе со следующими конструкциями библиотеки PFX:

  1. Класс Parallel - исключения могут возникнуть в параллельно исполняющихся итерациях циклов Parallel.For\Parallel.ForEach или в параллельно исполняющихся блоках кода при работе с Parallel.Invoke ;
  2. Класс Task - исключения, возникшие в теле задачи, будут повторно возбуждены в месте вызова метода Wait данной задачи. Кроме того, объект возникшего исключения доступен через свойство Task.Exception ;
  3. Класс Future<T> - исключения, возникшие в теле задачи, будут повторно возбуждены в месте вызова метода Wait (унаследованного от класса Task ) или в месте обращения к свойству Future<T>.Value ;
  4. PLINQ - из-за ленивого характера исполнения запросов PLINQ, исключения обычно возникают на этапе перебора элементов, полученных по запросу, а именно, при вызове методов ForAll(), ToList(), ToArray(), ToDictionary(), ToLookup(), MoveNext() и при работе с GetEnumerator() (например в цикле foreach ).

Имея представление о потенциальных местах, где могут возникнуть исключительные ситуации, при использовании параллельных конструкций библиотеки PFX, можно дать некоторые рекомендации о способах их перехвата и обработки. Общий принцип перехвата исключений в параллельной программе часто совпадает с аналогичным подходом для последовательных программ: перехватывать исключительные ситуации нужно, по возможности, как можно ближе к месту их возникновения. Например, если программа представляет собой целую иерархию задач, то, если есть возможность перехвата исключений в теле задач самого низкого уровня, то именно там и нужно применить механизм на базе конструкций try-catch, оставляя для главной задачи (корня иерархии) только функции управления исключительными ситуациями для задач более низкого уровня.

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

public static void ProcessImages(string path)
{
      Parallel.ForEach(GetImageFiles(path), imageFilePath =>
      {
          try
          {
               Bitmap bmp = new Bitmap(imageFilePath);
               ProcessImage(bmp);
           }
          catch (UnauthorizedAccessException uae)
          {
               HandleUnauthorizedAccessException(uae);
          }
          catch (FileNotFoundException fnfe)
          {
               HandleFileNotFoundExceptions(fnfe);
           }
       });
}

Здесь, возникновение исключительных ситуаций типа UnauthorizedAccessException и FileNotFoundException для одного из изображений не повлияет на параллельную обработку остальных изображений. При возникновении исключительных ситуаций другого типа, будет прервана обработка всех изображений.

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

var exceptions = new ConcurrentStack<Exception>();
Parallel.For(0, N, i=>
{
      try
      {
          Process(i);
      }
      catch(Exception exc) { exceptions.Push(exc); }
}

if (!exceptions.IsEmpty) throw new AggregateException(exceptions);
< Лекция 7 || Лекция 8: 12 || Лекция 9 >
Максим Полищук
Максим Полищук
"...Изучение и анализ примеров.
В и приведены описания и приложены исходные коды параллельных программ..."
Непонятно что такое - "В и приведены описания" и где именно приведены и приложены исходные коды.