Исключение catch, которое выбрасывается в другой поток

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

Есть ли какое-то время, когда я могу поймать это исключение в Method1, которое выбрано в Method2?

Ответ 1

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

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

    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);
        }
    }
    
  • В том же методе://Вы обрабатываете исключение в потоке вызывающего < вызывающего

    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.

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

  • //Вы обрабатываете исключение в потоке ребенка

    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();
        }
    }
    
  • Или//Вы обрабатываете исключение в потоке вызывающего < <

    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();
        }
    }
    

Ответ 2

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

Ответ 3

Вы можете использовать что-то вроде этого:

Thread thread = new Thread(delegate() {
    try
    {
        MyIPoller.Start();
    }
    catch(ThreadAbortException)
    {
    }
    catch(Exception ex)
    {
        //Handle
    }
    finally
    {
    }
});

Это гарантирует, что исключение не попадет в верхнюю часть потока.

Ответ 4

Самый простой способ совместного использования данных между различными потоками - это 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, by братья Albahari (2007), который также свободно доступен в Google Книгах, так же как и более новая версия книги, потому что он также охватывает пул потоков, передний план против фоновых потоков и т.д. и т.д., с красивым и простым примером кода. (Отказ от ответственности: у меня есть устаревшая копия этой книги)

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