从线程发送异常到主线程?

14

我希望将当�线程(�是主线程)的异常传递到主线程。

为什么?因为我在�一个线程(该线程使用定时器进行检查)中检查我的硬�,并且当HardLock��访问或无效时,我会创建一个自定义的异常,然�抛出该异常。但是这个异常工作得�太好。😢


我可以在program.cs中使用public static method并从另一个线程调用它(因为这是静态类),并使用application.exit终止应用程序,但这仅适用于终止应用程序而非执行其他操作! - Rev
6个回答

13

你最好将Thread替换为Task(.NET 4.0中的新功能)。Task类能够处理异常适当的传递,以便任何检查任务结果的线程都可以获得异常信息。

如果无法使用.NET 4.0,则可以从Rx扩展的CoreEx.dll中使用Exception.PrepareForRethrow扩展方法来保留异常的调用堆栈。您可以结合MaLio的建议使用SynchronizationContext将异常传递到另一个线程。


4

您可以将异常作为事件参数使用。
并在将异常发送给其他线程后进行处理。
代码示例。

public delegate void SendToMainDel(string threadName,Exception ex);
public event SendToMainDel SendToMainEv;

public void MySecondThread()
{
    try
    {
    ....
    }catch(Exception ex)
    {
         if(SendToMainEv!=null)
            SendToMainEv("MySecondThread",ex);
    }
}

...
    SendToMainEv += ReceiveOtherThreadExceptions;
...

public void ReceiveOtherThreadExceptions(string threadName,Exception ex)
{ 
   if(InvokeRequired)
   {
      BeginInvoke(new SendToMainDel(ReceiveOtherThreadExceptions), threadName, ex);
      return;
   }

   //there you can handle the exception
   //throw ex;
}

3

如果您需要一个线程来确保锁定不会持续太长时间或无效,那么在没有其他信息的情况下,似乎您的锁定已经失效了。

如果您真的需要向主线程抛出异常,请设置一个通信队列,从所有“工作线程”到您的“主线程”,并将您的整个工作线程包装在一个异常处理程序中,仅将异常附加到队列中,然后杀死该线程。您的主线程可以轮询队列以发现异常,并在纠正错误条件后重新启动已死亡的线程。


那么你怎么说呢!有任何样例吗? - Rev
一个很好的介绍线程和线程间通信的网站是http://www.albahari.com/threading/。 - Patrick

2

我认为如果你正在运行Windows Forms,可以在主窗体上使用Invoke、BeginInvoke来自try/catch块发送异常。或者你可以在主线程中创建事件处理程序/委托,并通过它将异常发送到主线程,以便主线程中的方法可以处理它。老实说,我还没有尝试过这些解决方案,但这些是我的第一想法。

另外,也许在主线程上创建一个WorkerQueue也是一个选择。它将作为backgroundWorker运行,当它收到新的异常时,会相应地进行处理... 如果你感兴趣,我可以给你一些小例子。

编辑:

public class JobQueue
{
    private Queue<Exception> pendingJobs = new Queue<Exception>();
    private Exception defaultJob = null;

    bool run = true;

    public void AddJob(Exception job)
    {
        pendingJobs.Enqueue(job);
    }

    public JobQueue()
    {
        defaultJob=null;
    }

    public void StopJobQueue()
    {
        run = false;
    }


    public void Run()
    {
        while (run)
        {

                Exception job = (pendingJobs.Count > 0) ? pendingJobs.Dequeue() : defaultJob;

                if (job!= null)
                {
                  ////what to do with current Exception
                 }

            Thread.Sleep(20); //I know this is bad...
        }


        pendingJobs.Clear();
    }



}
}

使用方法: 在你的主线程类中:
    private JobQueue m_jobQueue;

在 Initialize() 函数或任何主线程开始的地方:

   Backgroundworker bw = new Backgroundworker();
   bw.DoWork+= jobQueue.Run;
   bw.StartAsync();
    //m_jobQueue = new JobQueue();
    //    new Thread(new ThreadStart(jobQueue.Run)).Start(); 

发送异常请使用以下代码:

   m_jobQueue.AddJob(StackOverflowException);

请注意:

    m_jobQueue.StopJobQueue();

你从未分配defaultJob(或者我漏掉了什么?),它是用来干什么的?一个简单的替代Thread.Sleep的方法是使用AutoResetEvent(即使在Windows Mobile上也可以工作)。创建一个新的AutoResetEvent,执行while (pendingJobs.Count > 0) { job = pendingJobs.Dequeue(); },如果在出队时有新的作业入队,在其他while中放置一个resetEvent.WaitOne而不是Thread.Sleep。在Enqueue中,你可以执行resetEvent.Set()来唤醒Run()中的线程。 - Patrick
嗨,Patrick!你是对的。我只是基本上复制了我的项目,并删除了大部分代码以简化它。我的错。但至少作为一个例子,我认为它还是可以的。感谢你指出那些要点。 - nomail

2

将主表单的执行上下文引用通过委托或字段传递给线程。然后从您的线程通过该同步上下文调用一个方法(使用send或post)。执行上下文将确保它由UI线程处理。


2

你可能会发现将异常处理放在线程内并通过返回MyException.ToString() 在回调中传递异常消息和堆栈跟踪更容易。当我从另一个线程获取异常时,我需要的所有信息都在该字符串中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接