-->

catch exception that is thrown in different thread

2019-01-02 20:24发布

问题:

One of my method (Method1) spawns a new thread. That thread execute a method (Method2) and during exectution an exception is thrown. I need to get that exception information on the calling method (Method1)

Is there someway I can catch this exception in Method1 that is thrown in Method2?

回答1:

In .NET 4 and above, you can use Task<T> class instead of creating new thread. Then you can get exceptions using .Exceptions property on your task object. There are 2 ways to do it:

  1. In a separate method: // You process exception in some task's 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. In the same method: // You process exception in the caller's thread

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

Note that the exception which you get is AggregateException. All real exceptions are availible through ex.InnerExceptions property.

In .NET 3.5 you can use the following code:

  1. // You process exception in the child's thread

    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. Or // You process exception in the caller's thread

    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:

You can not catch the exception in Method1. You can, however, catch the exception in Method2 and record it to a variable that the original thread of execution can then read and work with.



回答3:

The simplest method to share data between different threads is shared data as follows (some is pseudo code):

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

You can read about this method in this nice introduction about multithreading, however, I preferred to read about this in the O'Reilly book C# 3.0 in a nutshell, by the brothers Albahari (2007), which is also freely accessible on Google Books, just like the newer version of the book, because it also covers thread pooling, foreground versus background threads, etc etc, with nice and simple example code. (Disclaimer: I own a worn-out copy of this book)

In case you are making a WinForms application, the use of shared data is especially handy, because the WinForm controls are not thread-safe. Using a callback to pass data from the worker thread back to a WinForm control the main UI thread needs ugly code with Invoke() to make that control thread-safe. Using shared data instead, and the single-threaded System.Windows.Forms.Timer, with a short Interval of say 0.2 seconds, you can easily send information from the worker thread to the control without Invoke.



回答4:

I had a particular problem in that I wanted to use items, containing controls, from an integration test suite, so have to create an STA thread. The code I ended up with is as follows, put here in case others have the same issue.

    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) { ... }

This is a direct paste of the code as-is. For other uses I would recommend supplying an action or function as a parameter and invoking that on the thread instead of hard-coding the called method.



标签: