我在.NET8中尝试
BlockingCollection<T>
(作为队列),有时我最终会遇到异常:
“System.InvalidOperationException:集合参数为空,并且已被标记为添加完成。”
代码示例:
private static void TestMethod()
{
using BlockingCollection<DummyClass> queue = new BlockingCollection<DummyClass>();
var task = Task.Run(() => //Produce
{
for (int i = 0; i < 10000000; i++)
{
queue.Add(new DummyClass());
}
queue.CompleteAdding();
});
int counter = 0;
try
{
while (!queue.IsCompleted) //Consume
{
DummyClass item = queue.Take(); // <-- Sometimes exception here
counter++;
}
Console.WriteLine($"counter={counter} ");
}
catch (Exception ex)
{
Console.WriteLine("Error:" + ex.ToString());
}
}
IsCompleted 状态“此集合是否已标记为完成添加且为空。”
因此,只要 CompleteAdding() 尚未被调用,Take() 就应该是阻塞的,并且当调用 CompleteAdding() 并且队列为空时,“ !queue.IsCompleted ”应该返回 false。
我错过了什么?
任何帮助将非常感激。
在 Windows 11 上的 VS2022 17.8.5 中运行。
你有一个线程竞赛;想象一下:
queue.CompleteAdding();
while (!queue.IsCompleted)
Take()
和繁荣
解决方案:使用
TryTake
食用
BlockingCollection<T>
的最佳方式是GetConsumingEnumerable
方法:
foreach (DummyClass item in queue.GetConsumingEnumerable())
{
// Process the consumed item
}
不幸的是,这种方便的方法在微软的文档和示例中并没有得到足够的重视。就我个人而言,我从来不需要该集合中的
IsCompleted
或 Take
成员。