阻塞集合上的 Take/TryTake 和 Add/TryAdd 之间的区别

Mud*_*uds 5 .net c# multithreading asynchronous blockingcollection

我一直在试图让我的头部周围的阻塞收集和我碰到Take()TryTake()Add()TryAdd()

我知道如果没有要取的项目,Take()将等到添加一个项目,类似地,Add()如果集合已达到其最大限制,它将等到项目被删除。

根据Josheph Albahari 关于并行编程的文章

“如果集合大小有界,Add 和 TryAdd 可能会阻塞;当集合为空时,Take 和 TryTake 会阻塞。”

因此Take()TryTake()两者都等待添加项目。那么,如果我们不提供任何超时或取消令牌,Take()和之间有什么区别TryTake(),不应该TryTake() return false直接而不是等待?和一样TryAdd()吗?

Bac*_*cks 6

TryTake不等待,如果集合什么都没有,它会立即返回falseTake将等待一个项目。

尝试:

如果集合为空,则此方法立即返回 false。

拿:

对 Take 的调用可能会阻塞,直到可以删除项目。

  • 这个答案是正确的。值得指出的是,这与问题中的引用不一致,该引用表示 `TryTake` _will 在集合为空时阻塞_。简单地说,这是错误的。 (2认同)

Pie*_*kel 6

Take将通过抛出一个InvalidOperationException. 如果您将异常选项卡配置为抛出捕获的异常,这会使调试变得有点困难。

因此,我确实尝试使用TryTake. 事实证明,BlockingCollection<T>.Take实际上确实使用了TryTake,但具有无限超时。所以,而不是写这个:

while (!queue.IsCompleted)
{
    object obj;

    try
    {
        obj = queue.Take();
    }
    catch (InvalidOperationException)
    {
        continue;
    }

    // Do something with obj.
}
Run Code Online (Sandbox Code Playgroud)

您可以TryTake按如下方式使用:

while (!queue.IsCompleted)
{
    if (!queue.TryTake(out var obj, Timeout.InfiniteTimeSpan))
        continue;

    // Do something with obj.
}
Run Code Online (Sandbox Code Playgroud)

使代码更清晰,并且不会抛出InvalidOperationException.