поймать исключение, которое выбрасывается в другой поток - PullRequest
105 голосов
/ 13 мая 2011

Один из моих методов (Method1) порождает новую тему.Этот поток выполняет метод (Method2), и во время выполнения генерируется исключение.Мне нужно получить информацию об этом исключении в вызывающем методе (Method1)

. Можно ли как-нибудь перехватить это исключение в Method1, которое выбрасывается в Method2?

Ответы [ 4 ]

173 голосов
/ 13 мая 2011

В .NET 4 и выше вы можете использовать класс Task<T> вместо создания нового потока. Затем вы можете получить исключения, используя свойство .Exceptions вашего объекта задачи. Есть 2 способа сделать это:

  1. В отдельном методе: // Вы обрабатываете исключение в некоторых задачах thread

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
            task.Start();
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    
        static void ExceptionHandler(Task<int> task)
        {
            var exception = task.Exception;
            Console.WriteLine(exception);
        }
    }
    
  2. В том же методе: // Вы обрабатываете исключение в потоке вызывающего

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.Start();
    
            try
            {
                task.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine(ex);    
            }
    
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    }
    

Обратите внимание, что вы получаете исключение AggregateException. Все реальные исключения доступны через ex.InnerExceptions property.

В .NET 3.5 вы можете использовать следующий код:

  1. // Вы обрабатываете исключение в дочернем потоке

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
            thread.Start();            
    
            Console.ReadLine();
        }
    
        private static void Handler(Exception exception)
        {        
            Console.WriteLine(exception);
        }
    
        private static void SafeExecute(Action test, Action<Exception> handler)
        {
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                Handler(ex);
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
    
  2. Или // Вы обрабатываете исключение в потоке вызывающего

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out exception));
    
            thread.Start();            
    
            thread.Join();
    
            Console.WriteLine(exception);    
    
            Console.ReadLine();
        }
    
        private static void SafeExecute(Action test, out Exception exception)
        {
            exception = null;
    
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
    
9 голосов
/ 13 мая 2011

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

0 голосов
/ 26 сентября 2018

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

    public Boolean? Dance(String name) {

        // Already on an STA thread, so just go for it
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) return DanceSTA(name);

        // Local variable to hold the caught exception until the caller can rethrow
        Exception lException = null;

        Boolean? lResult = null;

        // A gate to hold the calling thread until the called thread is done
        var lGate = new ManualResetEvent(false);

        var lThreadStart = new ThreadStart(() => {
            try {
                lResult = DanceSTA(name);
            } catch (Exception ex) {
                lException = ex;
            }
            lGate.Set();
        });

        var lThread = new Thread(lThreadStart);
        lThread.SetApartmentState(ApartmentState.STA);
        lThread.Start();

        lGate.WaitOne();

        if (lException != null) throw lException;

        return lResult;
    }

    public Boolean? DanceSTA(String name) { ... }

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

0 голосов
/ 03 марта 2015

Самый простой способ для обмена данными между различными потоками - это shared data следующим образом (некоторые псевдокод):

class MyThread
{
   public string SharedData;

   public void Worker()
   {
      ...lengthy action, infinite loop, etc...
      SharedData = "whatever";
      ...lengthy action...
      return;
   }
}

class Program
{
   static void Main()
   {
      MyThread m = new MyThread();
      Thread WorkerThread = new Thread(m.Worker);
      WorkerThread.Start();

      loop//or e.g. a Timer thread
      {
         f(m.SharedData);
      }
      return;
   }
}

Об этом методе вы можете прочитать в этом прекрасном введении о многопоточности , однако, я предпочел прочитать об этом в O'Reilly book C# 3.0 in a nutshell, написанном братьями Албахари (2007), который также свободно доступен в Google Книгах, как и в более новой версии книги, поскольку он также охватывает пул потоков,передний план против фоновых потоков и т. д., с красивым и простым примером кода.(Отказ от ответственности: у меня есть изношенная копия этой книги)

В случае, если вы создаете приложение WinForms, использование общих данных особенно удобно, поскольку элементы управления WinForm не являются поточно-ориентированными.При использовании обратного вызова для передачи данных из рабочего потока обратно в элемент управления WinForm главному потоку пользовательского интерфейса необходим некрасивый код с Invoke(), чтобы сделать этот элемент управления безопасным для потока.Используя совместно используемые данные и однопоточный System.Windows.Forms.Timer с коротким Interval, скажем, 0,2 секунды, вы можете легко отправлять информацию из рабочего потока в элемент управления без Invoke.

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