相关: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:我宁愿不接受读卡器线程上的锁,因为它位于热门路径上,而且我会非常经常地使用并释放锁。
答案 0 :(得分:3)
你将无法轻易逃脱。如果另一个线程在读者处理现有线程之前抛出异常,您将丢失异常。你需要的是一个同步队列:
try { ... stuff happens here... } catch (Exception ex1) { lock(queue) { queue.Enqueue(ex1); Monitor.PulseAll(queue); } }
并处理它:
while(!stopped) lock (queue) { while (queue.Count > 0) processException(queue.Dequeue()); Monitor.Wait(queue); }
答案 1 :(得分:2)
读取和写入引用都是原子的(参见C# Spec),我几乎可以肯定锁确实会造成内存障碍,所以你正在做的是可能安全。
但实际上只是在阅读时使用锁定。它保证工作;如果你们每次看到它都没有被锁定,你就知道出现了问题,如果锁定导致你出现性能问题,那么你就会经常检查标记方式,而这只是“正确的事情。”
答案 2 :(得分:1)
即使您可能只关心第一个例外,您可能仍然希望使用锁定至少有两个原因:
lock(queue)
会导致任何内存屏障操作。)lock(queue)
线程将导致内存屏障操作,如Eric在下面的评论中所指出的那样。 <击> 2。请记住References are not addresses(由Eric Lippert提供)(如果您假设参考是32位CLR中的32位地址,可以原子读取)。引用的实现可以更改为一些不透明的结构,这些结构在将来的CLR版本中可能无法原子读取(尽管我认为在可预见的将来不太可能发生:))并且您的代码将会中断。