【问题标题】:Sending an exception from thread to main thread?从线程发送异常到主线程?
【发布时间】:2011-03-25 03:04:23
【问题描述】:

我想将当前线程(该线程不是主线程)的异常传递给主线程。 为什么?因为我在另一个线程中检查了我的硬锁(该线程使用计时器进行检查),并且当HardLock 不可访问或无效时,我创建了一个由我自己定义的异常,然后抛出该异常。
所以这个异常不能很好地工作。 ;(

【问题讨论】:

  • 我可以在 program.cs 中使用public static method 并从另一个线程调用它(因为它是静态类),并使用application.exit 终止应用程序,但这仅适用于终止应用程序NOT做任何其他行为!

标签: c# multithreading exception exception-handling


【解决方案1】:

最好的办法是将Thread 替换为Task(.NET 4.0 中的新功能)。 Task 类将异常正确地编组到检查任务结果的任何线程。

如果无法使用 .NET 4.0,则来自 Rx extensions 的 CoreEx.dll 包含一个 Exception.PrepareForRethrow 扩展方法,该方法保留异常调用堆栈。您可以将它与 MaLio 的 SynchronizationContext 建议结合使用,将异常编组到另一个线程。

【讨论】:

    【解决方案2】:

    您可以将异常用作事件中的参数。
    并将异常发送到其他线程后处理。
    代码示例。

    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】:

      如果您需要一个线程来确保您的锁不会被持有太久或无效,那么您的锁定似乎被破坏了。

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

      【讨论】:

      • 那你怎么说!任何样品
      • 一个描述线程并在它们之间进行通信的好站点是albahari.com/threading
      【解决方案4】:

      将主窗体的执行上下文的引用传递给线程(通过委托或字段)。然后通过该同步上下文从引发异常的线程调用方法(发送或发布)。执行上下文将确保它被 ui 线程处理。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          只要我的 2 美分。

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

          附言。也许在主线程上创建一个 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 &gt; 0) { job = pendingJobs.Dequeue(); },以防一个新的工作在出队时被入队,在你的另一个while里面放一个resetEvent.WaitOne而不是Thread.Sleep。在Enqueue 中,您可以执行resetEvent.Set() 来唤醒Run() 中的那个。
          • 嗨帕特里克!你说的对。为了简单起见,我基本上只是将它从我的项目中复制出来,删除了大部分代码。我的错。但至少作为一个例子,我认为它会做。感谢您指出这些观点。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-02-17
          • 1970-01-01
          • 1970-01-01
          • 2014-10-06
          • 1970-01-01
          • 1970-01-01
          • 2013-03-13
          相关资源
          最近更新 更多