一名读者,多名作者
相关:如何从 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:我不想在读取器线程上锁定,因为它位于热路径上,并且我会非常非常频繁地锁定和释放锁定。
Related: How to catch exceptions from a ThreadPool.QueueUserWorkItem?
I am catching exceptions in background threads started by ThreadPool.QueueUserWorkItem(), and propagating them to the main thread via a shared instance variable.
The background threads do this:
try
{
... stuff happens here...
}
catch (Exception ex1)
{
lock(eLock)
{
// record only the first exception
if (_pendingException == null)
_pendingException = ex1;
}
}
There are multiple potential writers to _pendingException - multiple background threads - so I protect it with a lock.
In the main thread, must I take the lock before reading _pendingException
? Or can I simply do this:
if (_pendingException != null)
ThrowOrHandle();
EDIT:
ps: I would prefer to NOT take the lock on the reader thread because it is on the hot path, and I'd be taking and releasing the lock very, very often.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你将无法轻易逃脱。如果另一个线程在读者处理现有异常之前抛出异常,您将丢失异常。这里你需要的是一个同步队列:
并处理它:
You will not be able to get away this easy. You will lose exceptions if another thread throws it before the reader dealt with the existing one. What you need here is a synchronized queue:
And to process it:
对引用的读取和写入是原子的(请参阅 C# 规范< /a>)并且我几乎可以肯定锁确实会创建内存屏障,所以是的,您正在做的事情可能是安全的。
但实际上只是在你的阅读周围使用锁。保证可以工作;如果您看到它不是在锁中访问的,那么您就知道出了问题,如果锁导致了性能问题,那么您检查标志的次数太频繁了,而这正是“正确的做法”。
Reads and writes to references are atomic (See C# Spec) and I'm nearly certain that lock does create a memory barrier so yes what you are doing is probably safe.
But really just use the lock around your read. It's guaranteed to work; if you every see it accessed not in a lock you know something is wrong, if the lock is causing you performance issues then you're checking the flag way too often, and it's just the "right thing to do."
即使您可能只关心第一个异常,您可能仍然希望使用锁,至少有两个原因:
(我不确定在工作线程中调用(更新) 调用lock(queue)
会导致任何内存屏障操作)。lock(queue) 将导致内存屏障操作。
<罢工>2。请记住引用不是地址(作者:Eric Lippert)(如果您假设引用是 32 位 CLR 中可以原子读取的 32 位地址)。引用的实现可以更改为一些不透明的结构,这些结构在 CLR 的未来版本中可能无法以原子方式读取(尽管我认为这在可预见的将来不太可能发生:)),并且您的代码将会中断。
Even though you may only care about the first exception, you may still want to use lock for at least two reasons:
(I am not sure calling(update) Callinglock(queue)
in a worker thread will cause any memory barrier operation though).lock(queue)
in a worker thread will cause memory barrier operation as pointed out by Eric in the comment below.2. Please keep it mind that References are not addresses (by Eric Lippert) (if you are assuming references are 32-bit addresses in 32-bit CLR that can be read atomically). The implementation of references can be changed to some opaque structures that may not be read atomically in future release of CLR (even though I think it is not likely to happen in foreseeable future :)) and your code will break.