【问题标题】:How to empty a BlockingCollection如何清空 BlockingCollection
【发布时间】:2011-11-03 20:04:54
【问题描述】:

我有一个线程将项目添加到 BlockingCollection

在我正在使用的另一个线程上 foreach (var item in myCollection.GetConsumingEnumerable())

如果出现问题,我想打破我的 foreach 和我的方法,并清除 BlockingCollection 中留下的任何内容,但是我找不到解决方法。

有什么想法吗?

【问题讨论】:

    标签: c# .net c#-4.0 .net-4.0 task-parallel-library


    【解决方案1】:

    我正在使用这种扩展方法:

    public static void Clear<T>(this BlockingCollection<T> blockingCollection)
    {
        if (blockingCollection == null)
        {
            throw new ArgumentNullException("blockingCollection");
        }
    
        while (blockingCollection.Count > 0)
        {
            T item;
            blockingCollection.TryTake(out item);
        }
    }
    

    我想知道是否有更好、更简单的解决方案。

    【讨论】:

    • 使用新的out var 语法:while (blockingCollection.TryTake(out var _)){}
    • TryTake 而不是普通的旧Take 的任何特殊原因?在此示例中是否存在 try 版本会返回 false 的情况?
    • @Assimilater 在while 检查和TryTake 之间存在竞争条件。例如,另一个线程可以取出最后一个元素,Take 会引发InvalidOperationException
    • @PaoloMoretti 啊,我明白了。在我的用例中,我只有一个消费者,所以我没有想到 :)
    • 类似于 Kjellski 的评论,但不需要新的语法,这可以缩短为 T _; while (blockingCollection.TryTake(out _)){}
    【解决方案2】:

    只需取出所有剩余物品:

    while (collection.TryTake(out _)){}
    

    【讨论】:

    • 不知道_ 可以在out 语句中使用。双重福利! (想两次投票赞成这个答案:-)
    • 最好的!像魅力一样工作!
    【解决方案3】:

    可能使用GetConsumingEnumerable 的重载,它采用CancellationToken;然后,如果生产方出现任何问题,它可以取消消费者。

    【讨论】:

    • 我可以在 GetConsumingEnumerbale 的 foreach 中调用 CancellationToken.Cancel 吗?
    • 我可以在 GetConsumingEnumerable 中执行此操作,但它会引发 OperationCanceledException。一个简单的 break 将离开 foreach 循环,但 BlockingCollection 中仍会有项目
    • @Jon:我不是建议在消费者中取消,而是在 producer 中 - 我认为这是您检测到错误的地方。如果消费者注意到了这个问题,它不能把所有东西都吃光吗?它是否需要通知生产者说它不再需要任何物品?您甚至还需要相同的阻塞集合吗?你不能把它扔掉吗?
    • 我决定在消费者中使用中断,然后当我再次开始生产时,我只需创建一个 BlockingCollection 的新实例
    • 提醒粗心的人,GetConsumingEnumerable 将在集合为空时阻塞(等待更多条目),除非您之前调用过 CompleteAdding。它在示例代码块中,但在方法描述中没有提及。
    【解决方案4】:

    这对我有用

    while (bCollection.Count > 0)
    {
        var obj = bCollection.Take();
        obj.Dispose();
    }
    

    Take() 从集合中移除,您可以对您的对象调用任何清理,并且循环条件不会调用任何阻塞调用。

    【讨论】:

    • 这个答案出现在低质量审查队列中,大概是因为您没有解释代码。如果你确实解释了它(在你的回答中),你更有可能获得更多的支持——提问者实际上学到了一些东西!
    • 这是低效的,因为根据底层集合,您正在迭代每个元素以确定计数,只是为了删除第一个元素。此外,Dispose 与问题无关。
    【解决方案5】:
    BlockingCollection<T> yourBlockingCollection = new BlockingCollection<T>();
    

    我假设您的意思是清除您的阻止集合。我认为乔恩的回答更适合您的实际问题。

    【讨论】:

    • 我的意思是我想退出 foreach 循环并清空 BlockingCollection 中的任何内容
    • 您能否向我解释一下删除最后几项和只是:theBlockingCollectionUsedIntheForEachLoop = new BlockingCollection&lt;T&gt;(); 之间的区别是什么???
    【解决方案6】:

    只需清除您可以执行的集合:

    myBlockingCollection.TakeWhile<*MyObject*>(qItem => qItem != null);
    

    或者只是

    myBlockingCollection.TakeWhile<*MyObject*>(qItem => true);
    

    【讨论】:

    • TakeWhile 是 LINQ 移植,不会从集合中删除。它只是创建一个副本
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    • 2012-12-21
    • 2012-04-05
    • 1970-01-01
    相关资源
    最近更新 更多