捕获在不同线程中抛出的异常

时间:2011-05-12 20:03:39

标签: c# multithreading exception-handling

我的一个方法(Method1)产生一个新线程。 该线程执行方法(Method2)并在exectution期间抛出异常。 我需要获取有关调用方法(Method1

的异常信息

我是否可以在Method1 Method2中引发此异常?

4 个答案:

答案 0 :(得分:171)

.NET 4 及更高版本中,您可以使用Task<T>类而不是创建新线程。然后,您可以使用任务对象上的.Exceptions属性获取异常。 有两种方法可以做到:

  1. 在另一种方法中://您在某些任务的主题中处理异常

    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();
        }
    }
    
  3. 请注意,您获得的例外是AggregateException。所有真正的例外都可以通过ex.InnerExceptions属性获得。

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

答案 1 :(得分:9)

您无法在Method1中捕获异常。但是,您可以在Method2中捕获异常并将其记录到一个变量中,然后执行的原始执行线程可以读取并使用该变量。

答案 2 :(得分:1)

在不同线程之间共享数据的最简单方法是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;
   }
}

您可以在this nice introduction about multithreading中阅读有关此方法的内容,但是,我更倾向于在O'Reilly book C# 3.0 in a nutshell中阅读Albahari兄弟(2007年)中的内容,该内容也可以在Google图书上免费访问,喜欢本书的新版本,因为它还包括线程池,前台与后台线程等,以及简单的示例代码。 (免责声明:我拥有本书的旧版本)

如果您正在创建WinForms应用程序,使用共享数据尤其方便,因为WinForm控件不是线程安全的。使用回调将数据从工作线程传递回WinForm控件,主UI线程需要使用Invoke()的丑陋代码来使该控件具有线程安全性。使用共享数据和单线程System.Windows.Forms.Timer,短Interval比0.2秒,您可以轻松地将工作线程中的信息发送到控件而不Invoke

答案 3 :(得分:0)

我有一个特殊的问题,就是我想使用集成测试套件中包含控件的项目,因此必须创建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) { ... }

这是直接按原样粘贴代码。对于其他用途,我建议提供一个动作或函数作为参数,然后在线程上调用它,而不是硬编码所调用的方法。