阻塞集合中Take/TryTake和Add/TryAdd的区别

7
我一直在努力理解Blocking Collection,我发现了Take()TryTake(),还有Add()TryAdd()
我了解到,如果没有要取的项,Take()将等待直到添加项,类似地,如果集合达到其最大限制,Add()将等待直到项被移除。
根据Josheph Albahari关于并行编程的文章,

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

所以,Take()TryTake()都会等待添加一个项目。那么,如果我们没有提供任何超时或取消标记,Take()TryTake()之间有什么区别?TryTake()不应该立即返回false而不是等待吗?TryAdd()也是同样的情况吗?

1
如果你阅读文档,它会说(对于TryAdd):“如果集合是有界集合,并且已满,该方法将立即返回false,而不添加该项。” TryTake也是一样。当然,这适用于不接受任何超时的重载。 - Evk
3个回答

9

TryTake 不会等待,如果集合中没有任何内容,它会立即返回falseTake 会等待一个项目。

TryTake:

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

Take:

调用Take可能会阻塞,直到有一个项目可供移除。


2
这个答案是正确的。值得指出的是,这与问题中的引用相矛盾,引用中说 TryTake 在集合为空时会阻塞。简单地说,那是错误的。 - bornfromanegg
是的,这样说很有道理,之前对提到的资源描述感到困惑。谢谢你的澄清。 - Muds

6

Take 会通过抛出 InvalidOperationException 信号来表示队列已完成,如果你将异常选项卡配置为在捕获到异常时抛出,则会使调试变得更加困难。

因此,我尝试使用 TryTake。结果发现,BlockingCollection<T>.Take 实际上使用了 TryTake,但是超时时间设置为无限。因此,不需要编写以下代码:

while (!queue.IsCompleted)
{
    object obj;

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

    // Do something with obj.
}

你可以按照以下方式使用TryTake
while (!queue.IsCompleted)
{
    if (!queue.TryTake(out var obj, Timeout.InfiniteTimeSpan))
        continue;

    // Do something with obj.
}

使代码更加简洁,避免抛出 InvalidOperationException 异常。

1
我偶然发现了这个问题,我认为Microsoft的这篇文档真的很有帮助,可以帮助我们弄清楚在科学背后发生了什么。
如果有项目存在于有界集合中,则尝试添加或尝试取出操作将返回false,表示已达到有界集合的最大容量或超时时间已过。这使得线程可以进行其他一些有用的工作一段时间,然后稍后再次尝试检索新项目或尝试添加无法先前添加的相同项目。 如何从BlockingCollection中单独添加和获取项目

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接