为什么当 IsEmpty == true 时 ConcurrentQueue.Count 不返回 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
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 whyIsEmpty
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. ConcurrentEnqueue
andTryDequeue
operations are changing the count, soCount
has to retry; unless the queue is transitioning between the empty and non-empty states, the return value ofIsEmpty
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.让我给你看一个夸大的例子:
如果他们像这样实现
Count
属性:现在当最终的Count为0时,它花费了10秒(比以前少了5秒!太棒了! ),但对于那些 Count 为 1/2/more 的情况,它的成本比以前更高,因为检查
IsEmpty
需要时间!所以优化不是一个好主意,因为IsEmpty
需要时间。如果IsEmpty
直接从字段读取,那就太好了。编辑我通过反射器检查了
IsEmpty
和Count
的实现,两者都很昂贵。显然,仅检查IsEmpty
是否为 0 计数会降低平均性能速度。Let me show you an overstating example:
If they implement the
Count
property like this: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, becauseIsEmpty
takes time. It will be good ifIsEmpty
is reading from a field directly.EDIT I checked the implementaion of both
IsEmpty
andCount
via reflector, both are expensive. Obviously checkingIsEmpty
for 0 count only will reduce the average performance speed.了解并发结构如何工作非常重要。
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.
IsEmpty
提供了一些线程并发性,如果您获取Count
值并进行比较,则它在您的线程上,但队列可能会更改。MSDN 说:
IsEmpty
provides some thread concurrency that if you obtain theCount
value and compare it, its on your thread, but the queue could be changed.MSDN says: