【问题标题】:One reader, many writers一位读者,许多作者
【发布时间】:2010-12-12 21:01:26
【问题描述】:

相关:How to catch exceptions from a ThreadPool.QueueUserWorkItem?

我在ThreadPool.QueueUserWorkItem() 启动的后台线程中捕获异常,并通过共享实例变量将它们传播到主线程。

后台线程这样做:

try
{
    ... stuff happens here...
}
catch (Exception ex1)
{
    lock(eLock) 
    {
        // record only the first exception
        if (_pendingException == null) 
            _pendingException = ex1;
    }
}

_pendingException 有多个潜在的编写者——多个后台线程——所以我用锁保护它。

在主线程中,我必须在读取_pendingException之前获取锁吗?或者我可以简单地这样做吗:

if (_pendingException != null)
    ThrowOrHandle(); 

编辑:
ps:我宁愿不锁定读者线程,因为它在热路径上,而且我会非常非常频繁地获取和释放锁定。

【问题讨论】:

  • 你描述的zdnet不是你吗?
  • 除了传回的异常之外,您是否正在做任何事情?或者您可以使用 bool 来标记发生了错误吗?
  • zdnet?不,我什至不知道那是什么。嗯,承租人……我是不是在使用例外……嗯,是的。我从主线程重新抛出它。

标签: c# .net multithreading exception queueuserworkitem


【解决方案1】:

你不会这么容易逃脱的。如果另一个线程在读者处理现有异常之前抛出异常,您将丢失异常。你需要的是一个同步队列:

尝试 { ......这里发生了一些事情...... } 捕获(异常 ex1) { 锁(队列) { queue.Enqueue(ex1); Monitor.PulseAll(队列); } }

并对其进行处理:

而(!停止) 锁(队列) { while (queue.Count > 0) processException(queue.Dequeue()); Monitor.Wait(队列); }

【讨论】:

  • 我真的只想要第一个例外 - 这是一种快速失败的方法。没有恢复。所以我不在乎我是否错过了例外 2..n。我只想要第一个。
【解决方案2】:

对引用的读取和写入是原子的(请参阅C# Spec),我几乎可以肯定锁确实会创建内存屏障,所以是的,您所做的事情可能是安全的。

但实际上只是在你的阅读周围使用锁。保证可以工作;如果您每个人都看到它不是在锁中访问的,那么您就知道出了点问题,如果锁导致您出现性能问题,那么您检查标志的方式太频繁了,这只是“正确的做法”。

【讨论】:

  • 问题是,主线程是读取那个 _pendingException 字段的线程。我宁愿不要在阅读器线程上获取锁,因为它位于热路径上,而且我会非常频繁地获取和释放锁。
  • 为什么不想在主线程上使用锁?您是否尝试过并发现它太慢或者这是过早的优化?如果它给您带来了问题,那么您过于频繁地轮询该标志,应该选择不同的通知策略,例如出错时调用委托而不是轮询标志。
【解决方案3】:

即使您可能只关心第一个异常,您可能仍然希望使用 lock 至少有两个原因:

  1. 在多核 CPU 中,如果不将变量设为 volatile(或执行任何内存屏障操作),则在不同核上运行的线程有时可能会看到不同的值。 (我不确定在工作线程中调用 lock(queue) 会导致任何内存屏障操作)。 (更新) 在工作线程中调用 lock(queue) 会导致内存Eric 在下面的评论中指出的屏障操作。

2。请记住References are not addresses(由 Eric Lippert 撰写)(如果您假设引用是 32 位 CLR 中可以原子读取的 32 位地址)。引用的实现可以更改为一些不透明的结构,这些结构在未来的 CLR 版本中可能无法自动读取(尽管我认为在可预见的未来不太可能发生 :)),您的代码将会中断。

【讨论】:

  • 这是不正确的。规范明确指出,在 C# 的所有实现中,引用的读取和写入必须是原子的。有关详细信息,请参阅规范的第 5.5 节。
  • 更新(删除)Eric Lippert 指出的错误信息。感谢您的更正,我很抱歉这么晚的回复:)。如果数字 1 仍然不正确,请告诉我。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多