为什么ConcurrentQueue <T> .Count在IsEmpty == true时返回0?

Dan*_*eny 11 .net c# collections

我正在阅读James Michael Hare的博客中关于.NET 4中新的并发集合类的内容,并且正在讨论ConcurrentQueue<T>页面说:

但是,仍然建议您对空检查调用IsEmpty而不是将Count与零进行比较.

我很好奇 - 如果有理由使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

如果它可以很容易地"修复"而没有副作用,这似乎很奇怪吗?

Bra*_*ger 13

ConcurrentQueue<T>是无锁的,并使用旋转等待来实现高性能并发访问.实现只需要完成更多工作以返回确切的计数而不是检查是否没有项目,这IsEmpty就是推荐的原因.

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

我写了一个简单的多线程测试应用程序,它显示Count速度慢了约20%(同时具有持续争用和无争用); 但是,这两种属性每秒可以调用数百万次,因此在实践中任何性能差异都可能完全忽略不计.

  • 也许是因为通过等待两次以加速边缘情况来减慢Count的每次调用都是不明智的. (3认同)

Che*_*hen 7

让我向您展示一个夸大的例子:

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... */  }
}
Run Code Online (Sandbox Code Playgroud)

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

public int Count
{
   get
   {
       if (IsEmpty) return 0;
       //original implementation here
   }
}
Run Code Online (Sandbox Code Playgroud)

现在当最终的Count为0时,它花费10s(比之前少5s!太棒了!),但是对于那些Count是1/2 /更多,它比以前花费更多,因为检查IsEmpty成本时间!所以优化不是一个好主意,因为IsEmpty需要时间.如果IsEmpty直接从一个领域读书会很好.

编辑我检查了两者IsEmptyCount通过反射器的实现,两者都很昂贵.显然只检查IsEmpty0计数会降低平均性能速度.