为什么当 IsEmpty == true 时 ConcurrentQueue.Count 不返回 0?

发布于 2024-10-19 10:29:27 字数 807 浏览 1 评论 0原文

我在 James Michael Hare 的博客 上阅读了有关 .NET 4 中新的并发集合类的信息,以及谈论 ConcurrentQueue的页面说:

但是,仍然建议 对于空支票,您可以调用 IsEmpty 而不是将计数与零进行比较。

我很好奇 - 如果有理由使用 IsEmpty 而不是将 Count 与 0 进行比较,为什么该类不在执行任何昂贵的计数工作之前在内部检查 IsEmpty 并返回 0

例如:

public int Count
{
    get
    {
        // Check IsEmpty so we can bail out quicker
        if (this.IsEmpty)
            return 0;

        // Rest of "expensive" counting code
    }
}

如果它可以如此轻松地“修复”并且没有副作用,那么提出这个建议似乎很奇怪?

I was reading about the new concurrent collection classes in .NET 4 on James Michael Hare's blog, and the page talking about ConcurrentQueue<T> says:

It’s still recommended, however, that
for empty checks you call IsEmpty
instead of comparing Count to zero.

I'm curious - if there is a reason to use IsEmpty instead of comparing Count to 0, why does the class not internally check IsEmpty and return 0 before doing any of the expensive work to count?

E.g.:

public int Count
{
    get
    {
        // Check IsEmpty so we can bail out quicker
        if (this.IsEmpty)
            return 0;

        // Rest of "expensive" counting code
    }
}

It seems strange to suggest this if it could be "fixed" so easily with no side-effects?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(4

妥活 2024-10-26 10:29:27

ConcurrentQueue 是无锁的,并使用自旋等待来实现高性能并发访问。该实现只需要做更多的工作来返回准确的计数,而不是检查是否没有项目,这就是建议使用 IsEmpty 的原因。

直观上,您可以认为 Count 必须在没有其他客户端更新队列时等待一个时间片,以便拍摄快照,然后对该快照中的项目进行计数。 IsEmpty 只需检查是否至少有一项。并发的 Enqueue 和 TryDequeue 操作正在改变计数,因此 Count 必须重试;除非队列在空和非空状态之间转换,否则 IsEmpty 的返回值不会因并发操作而更改,因此不必等待。

我编写了一个简单的多线程测试应用程序,结果显示 Count 慢了约 20%(有持续争用和无争用);然而,这两个属性每秒都可以被调用数百万次,因此在实践中任何性能差异都可能完全忽略不计。

ConcurrentQueue<T> is lock-free and uses spin waits to achieve high-performance concurrent access. The implementation simply requires that more work be done in order to return the exact count than to check if there are no items, which is why IsEmpty is recommended.

Intuitively, you can think of Count having to wait for a timeslice when no other clients are updating the queue, in order to take a snapshot and then count the items in that snapshot. IsEmpty simply has to check if there is at least one item or not. Concurrent Enqueue and TryDequeue operations are changing the count, so Count has to retry; unless the queue is transitioning between the empty and non-empty states, the return value of IsEmpty isn't changed by concurrent operations, so it doesn't have to wait.

I wrote a simple multi-threaded test app which showed that Count was ~20% slower (with both constant contention and no contention); however, both properties can be called millions of times per second so any performance difference is likely to be completely negligible in practice.

夏雨凉 2024-10-26 10:29:27

让我给你看一个夸大的例子:

public bool IsEmpty
{
   get { /* always costs 10s here */  }
}

public int Count
{
   get { /* always costs 15s here, no matter Count is 0 or 1 or 2... */  }
}

如果他们像这样实现Count属性:

public int Count
{
   get
   {
       if (IsEmpty) return 0;
       //original implementation here
   }
}

现在当最终的Count为0时,它花费了10秒(比以前少了5秒!太棒了! ),但对于那些 Count 为 1/2/more 的情况,它的成本比以前更高,因为检查 IsEmpty 需要时间!所以优化不是一个好主意,因为 IsEmpty 需要时间。如果 IsEmpty 直接从字段读取,那就太好了。

编辑我通过反射器检查了IsEmptyCount的实现,两者都很昂贵。显然,仅检查 IsEmpty 是否为 0 计数会降低平均性能速度。

Let me show you an overstating example:

public bool IsEmpty
{
   get { /* always costs 10s here */  }
}

public int Count
{
   get { /* always costs 15s here, no matter Count is 0 or 1 or 2... */  }
}

If they implement the Count property like this:

public int Count
{
   get
   {
       if (IsEmpty) return 0;
       //original implementation here
   }
}

Now when the final Count is 0, it costs 10s(5s less than before!great!), but for those Count is 1/2/more, it costs more than before, because checking IsEmpty costs time! So the optimization is not a good idea, because IsEmpty takes time. It will be good if IsEmpty is reading from a field directly.

EDIT I checked the implementaion of both IsEmpty and Count via reflector, both are expensive. Obviously checking IsEmpty for 0 count only will reduce the average performance speed.

吾家有女初长成 2024-10-26 10:29:27

了解并发结构如何工作非常重要。

if (isEmpty()) ...//doWhatever

如果您有并发结构,则检查接近无操作,因为一切都可以在 isEmpty 和任何后续操作之间更改。

Count 遍历节点(已经将近 6 年没有使用 c#,但 java 模拟做了同样的事情)来计算,因此这是一个昂贵的调用。
直接回答:在 Count 之前检查 isEmpty 会产生额外的内存栅栏,并且实际上不会实现任何目标。
编辑:如果不清楚。队列为空时的计数成本与 isEmpty 完全相同,但是当队列时,成本会很高!

对于并发结构,类似于 isEmpty 的 Count 几乎没有意义,因为调用的结果可能没有用并且发生很大变化。

Understanding how concurrent structures work is very important.

if (isEmpty()) ...//do whatever

if you have concurrent structure the check is close to no-op since everything can change between isEmpty and any subsequent operation.

Count iterates through the nodes (have not used c# for almost 6 years, but the java analog does the same) to calculate, so it is an expensive call.
Straight answer: Checking isEmpty before Count will incur additional memory fence and effectively achieve nothing.
Edit: if unclear. Count when the queue is empty costs exactly as isEmpty, however it costs a lot when the queue is not!

Count similar to isEmpty for concurrent structures has little to no meaning since the result of the call may not be useful and greatly changed.

说谎友 2024-10-26 10:29:27

IsEmpty 提供了一些线程并发性,如果您获取 Count 值并进行比较,则它在您的线程上,但队列可能会更改。

MSDN 说:

用于判断是否集合
包含任何项目,使用此
推荐财产而不是
从中检索项目数量
Count 属性并将其与
0. 但是,由于该集合旨在并发访问,
可能是另一个线程的情况
之后将修改集合
IsEmpty 返回,从而使
结果。

IsEmpty provides some thread concurrency that if you obtain the Count value and compare it, its on your thread, but the queue could be changed.

MSDN says:

For determining whether the collection
contains any items, use of this
property is recommended rather than
retrieving the number of items from
the Count property and comparing it to
0. However, as this collection is intended to be accessed concurrently,
it may be the case that another thread
will modify the collection after
IsEmpty returns, thus invalidating the
result.

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