BlockingCollection 使用

创建 BlockingCollection 的实例时通过构造函数指定最大容量,从而限制集合中项目的数量。
BlockingCollection 的最大容量设置为 5,这意味着生产者在尝试添加超过 5 个项目时会被阻塞,直到集合中有空间。消费者从集合中取出项目并处理它们,从而为生产者腾出空间。

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        int maxCapacity = 5;
        BlockingCollection<int> collection = new BlockingCollection<int>(maxCapacity);

        Task producer = Task.Run(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                collection.Add(i);
                Console.WriteLine($"Produced: {i}");
            }
            collection.CompleteAdding();
        });

        Task consumer = Task.Run(() =>
        {
            foreach (var item in collection.GetConsumingEnumerable())
            {
                Console.WriteLine($"Consumed: {item}");
                Thread.Sleep(500); // Simulate some work
            }
        });

        Task.WaitAll(producer, consumer);
    }
}

BlockingCollection.IsCompleted 属性用于指示集合是否已经完成添加并且是否已经消耗了所有数据。换句话说,它返回 true 的条件是集合已经调用了 CompleteAdding() 并且集合为空。因此,IsCompleted 包含了集合为空的判断。

如果你想判断是否已经调用了 CompleteAdding() 方法,但集合中仍然有值,你可以使用 BlockingCollection.IsAddingCompleted 属性。这两个属性的区别在于:

IsAddingCompleted:当调用了 CompleteAdding() 方法后,此属性将返回 true,即使集合中仍然有未消费的元素。
IsCompleted:当调用了 CompleteAdding() 方法且集合为空时,此属性将返回 true

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        BlockingCollection<int> collection = new BlockingCollection<int>();

        Task producer = Task.Run(() =>
        {
            for (int i = 0; i < 5; i++)
            {
                collection.Add(i);
                Console.WriteLine($"Produced: {i}");
            }
            collection.CompleteAdding();
        });

        Task consumer = Task.Run(() =>
        {
            while (!collection.IsCompleted)
            {
                if (collection.TryTake(out int item, TimeSpan.FromSeconds(1)))
                {
                    Console.WriteLine($"Consumed: {item}");
                }
                else if (collection.IsAddingCompleted)
                {
                    Console.WriteLine("Adding is completed but items still remain.");
                }
            }
            Console.WriteLine("Collection is completed and empty.");
        });

        Task.WaitAll(producer, consumer);
    }
}

在这个示例中,消费者任务在一个循环中检查 collection.IsCompleted 属性。如果集合未完成,尝试从集合中取出一个项目(带超时的 TryTake)。如果 TryTake 返回 false,并且 IsAddingCompleted 返回 true,这意味着已经调用了 CompleteAdding(),但集合中仍有未消费的元素。

当集合完成且为空时,collection.IsCompleted 将返回 true,消费者任务将输出 "Collection is completed and empty." 并结束。

BlockingCollection.TryTake 方法具有两种不同的行为,取决于是否提供了超时参数:

无参数的 TryTake 方法:

不会阻塞。如果集合中没有项,立即返回 false。
带有超时参数的 TryTake 方法:

可以阻塞。它会等待指定的超时期限以尝试从集合中获取一个项。在此期间,如果集合变得非空,则返回该项;否则,在超时期限过后返回 false。

由于 BlockingCollection 是线程安全的,多个线程可以安全地并发访问集合,无需担心多线程问题。

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        BlockingCollection<int> collection = new BlockingCollection<int>(10); // 设置容量限制为10

        // Producer tasks
        Task producer1 = Task.Run(() => Produce(collection, 0));
        Task producer2 = Task.Run(() => Produce(collection, 100));

        // Consumer tasks
        Task consumer1 = Task.Run(() => Consume(collection));
        Task consumer2 = Task.Run(() => Consume(collection));

        Task.WaitAll(producer1, producer2, consumer1, consumer2);
    }

    static void Produce(BlockingCollection<int> collection, int start)
    {
        for (int i = start; i < start + 10; i++)
        {
            collection.Add(i);
            Console.WriteLine($"Produced: {i}");
            Thread.Sleep(100); // 模拟一些工作
        }
        collection.CompleteAdding();
    }

    static void Consume(BlockingCollection<int> collection)
    {
        foreach (var item in collection.GetConsumingEnumerable())
        {
            Console.WriteLine($"Consumed: {item}");
            Thread.Sleep(150); // 模拟一些工作
        }
        Console.WriteLine("Collection is completed and empty.");
    }
}

通过这种方式,你可以在多个线程中安全地使用 Add 和 Take 操作,并且 BlockingCollection 会处理所有必要的同步问题

posted @   Josen_Earth  阅读(88)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示