【问题标题】:Safe to throw an exception created on another thread C#安全地抛出在另一个线程 C# 上创建的异常
【发布时间】:2012-07-04 19:23:02
【问题描述】:

我想在线程 A 中捕获异常,然后将异常对象传递给线程 B,然后从线程 B 中抛出。这样安全吗?

线程A

try {
    // Code that throws exceptions
} catch (Exception e) {
    sendToOtherThread(e);
}

线程 B

Exception e = receiveException();
throw e;

编辑

为了清楚起见:我了解线程的工作原理,以及我应该如何将对象引用从一个线程传递到另一个线程。问题更多的是从一个没有创建异常对象的线程抛出异常对象是否安全,或者Exception 类本身或.NET 处理它的方式是否存在任何问题。

【问题讨论】:

  • 是的,但是(可能)非常没用,因为你不能重新抛出,所以你会覆盖原始的堆栈跟踪。如果这不是您想要的,则必须包装异常并抛出新异常。
  • 我很好奇你们对sendToOtherThreadreceiveException的实现。
  • 假设这两个方法按原样传递对象引用。所以两个线程中的 e 都指向同一个对象。

标签: c# multithreading exception thread-safety


【解决方案1】:

可以重新抛出从另一个线程接收到的异常(例如,在 BackgroundWorker 的 RunWorkerCompleted 事件处理程序中),但更常见的做法是将其包装在另一个异常中,以保留堆栈跟踪:

private void backgroundWorker1_RunWorkerCompleted(
    object sender, RunWorkerCompletedEventArgs e)
{
    // First, handle the case where an exception was thrown.
    if (e.Error != null)
    {
        throw new SomeException("... message ...", e.Error);
    }
    ...
}

【讨论】:

    【解决方案2】:

    如果您考虑使用 .NET 4.0,则在加入 Task 时,Task 引发的任何异常都会自动传播到父线程。这是设计使然,应该是完全安全的。看看这里:http://msdn.microsoft.com/en-us/library/dd997415.aspx

    【讨论】:

    • 谢谢你的回答也不错,但我只能投一个
    【解决方案3】:

    你需要改变这个:

    在线程 B 中

    Exception e = receiveException();
    throw new Exception("Write something here, for example: 'Received from other thread'", e); // so you keep the stack trace
    

    但我会注意一些警告(我不完全确定这是 100% 安全的)。

    【讨论】:

    • 感谢重新包装的建议。但是这个问题仍然没有答案:-/
    • 包装您的异常以跟踪引发异常的原始线程是一个好主意,但是正如 OP 所说,这不是问题的答案
    【解决方案4】:

    它应该是安全的,但很可能它会终止线程 B。

    您的解决方案仅取决于您如何将异常对象从线程 A 传递到线程 B。 如果您将其放入共享资源中,则需要锁定和释放该特定共享资源。除此之外,您的代码没有任何不安全之处。

    只要您不访问线程中的任何共享资源,其他一切都应该是安全的。

    【讨论】:

      【解决方案5】:

      Exception 是一个类,因此是一个标准引用类型,如果你想在线程之间传递它(这听起来有点奇怪)你需要确保没有其他人试图修改实例(特别是如果你创建你的自己的可变异常类型)。

      解决方案 A
      你可以做一个生产者-消费者拉模型

      BlockingCollection<Exception> bc = new BlockingCollection<Exception>();
      
      // thread A - Producer
      try
      {
          ...
      }
      catch(Exception ex)
      {
          bc.Add(ex);
      }
      
      
      //thread B - Consumer
      try
      {
          // Consume bc
          while (true)
          {
              var ex = bc.Take();
              //thread sleep
          }
      }
      catch (InvalidOperationException)
      {
          // IOE means that Take()
          // was called on a completed collection and no
          // No more exceptions, restart checking    
      }
      

      解决方案 B
      但可能更好的主意是不使用拉模型,因为在这种情况下它会浪费资源,而是使用线程安全的可观察集合的推模型。

      【讨论】:

        猜你喜欢
        • 2016-07-28
        • 2012-01-11
        • 2022-01-09
        • 2011-06-25
        • 1970-01-01
        • 2012-02-10
        • 2022-09-28
        • 2011-11-14
        • 2015-02-23
        相关资源
        最近更新 更多