如何将异常显式传递给c#中的主线程

时间:2012-01-08 10:43:13

标签: c# multithreading

我很熟悉一个线程中抛出的异常通常无法在另一个线程中捕获的事实。 我怎样才能将错误转移到主线程?

public static void Main()
{
   new Thread (Go).Start();
}

static void Go()
{
  try
  {
    // ...
    throw null;    // The NullReferenceException will get caught below
    // ...
  }
  catch (Exception ex)
  {
    // Typically log the exception, and/or signal another thread
    // that we've come unstuck
    // ...
  }
}

5 个答案:

答案 0 :(得分:7)

如果您可以使用C#4.0,则可以使用Task代替ThreadTask中未捕获的异常会自动传播到加入的线程。请参阅此处:http://msdn.microsoft.com/en-us/library/dd997415.aspx了解此规则的示例和例外情况。

答案 1 :(得分:6)

如果您使用的是.NET 4,则有更好的方法可以使用Tasks执行此操作,但假设您需要使用Threads ...

如果您的示例是控制台应用程序,那么您的Main方法将在Go开始执行之前退出。因此,抛出异常时,您的“主线程”可能不存在。要停止此操作,您需要进行一些同步。

这样的事情应该做:

static Exception _ThreadException = null;

public static void Main()
{
    var t = new Thread ( Go );
    t.Start();

    // this blocks this thread until the worker thread completes
    t.Join();

    // now see if there was an exception
    if ( _ThreadException != null ) HandleException( _ThreadException );
}

static void HandleException( Exception ex )
{
    // this will be run on the main thread
}

static void Go()
{
    try
    {
        // ...
        throw null;    // The NullReferenceException will get caught below
        // ...
    }
    catch (Exception ex) 
    {
        _ThreadException = ex;
    }
}

如果这是一个UI应用程序,事情会更容易一些。您需要将对UI线程的一些引用传递给Go方法,以便它知道将异常发送到何处。执行此操作的最佳方法是传递UI线程的SynchronizationContext

这样的事情会起作用:

public static void Main()
{
    var ui = SynchronizationContext.Current;
    new Thread ( () => Go( ui ) ).Start();
}

static void HandleException( Exception ex )
{
    // this will be run on the UI thread
}

static void Go( SynchronizationContext ui )
{
    try
    {
        // ...
        throw null;    // The NullReferenceException will get caught below
        // ...
    }
    catch (Exception ex) 
    {
        ui.Send( state => HandleException( ex ), null );
    }
}

答案 2 :(得分:2)

您可以在应用级别订阅常规unhandled exceptions

答案 3 :(得分:2)

这是一个非常古老的主题,但我对这个问题有一个原创的方法,所以我会把我的解决方案放在这里。

我的主要目标是主线程在子线程获得异常时被异常中断,而不是像其他答案中提出的那样监视或等待。 正如在其他线程中所说的那样,我们不能用传统方式做到这一点,但我们可以从其他线程向主线程发送中断。

向主线程发送中断会在主线程上生成ThreadInterruptedException。因此,在主线程中捕获它并检查由另一个线程引发的任何标志也将执行异常细节传输。

这是一个最小的例子(您可以将其分解为类,并在以后做更好的设计)

static void Main(string[] args)
    {


        // initialize the second thread ***************************
        Exception exFromThread = null;

        Thread thread = new Thread((mainThread) =>
        {
            Thread.Sleep(1000);
            exFromThread = new Exception("Exception from other thread");
            ((Thread)mainThread).Interrupt(); // makes the main thread get exception
        });

        thread.Start(Thread.CurrentThread);
        // ********************************************************

        try
        {
            // This loop represents your main thread logic
            for (int i = 0; true; i++)
            {
                Thread.Sleep(500);
                Console.WriteLine("main thread logic: " + i);
            }
        }
        catch (ThreadInterruptedException ex)
        {
            Console.WriteLine("Thread have been interrupted");
            Console.WriteLine(exFromThread.Message);
        }

        Console.WriteLine("Press any key..");

        Console.ReadLine();
    }

答案 4 :(得分:0)

我假设您所谓的“主线程”应该轮询或等待异常。这可以通过队列和信号量来完成。

catch( Exception ex )
{
   lock( queueLock )
   {
      queue.Enqueue( ex )
      semaphore.Release();
   }

}

在主线程中,您可以轮询或等待信号量。