一名读者,多名作者

发布于 2024-08-10 02:44:28 字数 827 浏览 9 评论 0原文

相关:如何从 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

薄凉少年不暖心 2024-08-17 02:44:28

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

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);
    }

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:

try
{
    ... stuff happens here...
}
catch (Exception ex1)
{
    lock(queue)
    {
        queue.Enqueue(ex1);
        Monitor.PulseAll(queue);
    }
}

And to process it:

while(!stopped)
    lock (queue)
    {
        while (queue.Count > 0)
            processException(queue.Dequeue());
        Monitor.Wait(queue);
    }
夏见 2024-08-17 02:44:28

对引用的读取和写入是原子的(请参阅 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."

-黛色若梦 2024-08-17 02:44:28

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

  1. 在多核 CPU 中,在不使变量为 易失性(或执行任何内存屏障操作)的情况下,可能会有这样的时刻:在不同内核上运行的线程可能会看到不同的值。 (我不确定在工作线程中调用 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:

  1. In multi-core CPUs, without making a variable volatile (or performing any memory barrier operation) , there might be a moment when threads running on different cores may see different values. (I am not sure calling lock(queue) in a worker thread will cause any memory barrier operation though). (update) Calling 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文