当前位置:首页 > C# > 正文

C# BlockingCollection详解(阻塞集合使用教程:轻松掌握多线程安全队列)

在C#多线程编程中,如何安全地在线程之间传递数据是一个常见且关键的问题。.NET 提供了 BlockingCollection<T> 类,它是一个线程安全的集合,特别适用于 生产者-消费者模式。本文将带你从零开始,深入浅出地学习 C# BlockingCollection 的基本用法、核心特性以及实际应用场景。

C# BlockingCollection详解(阻塞集合使用教程:轻松掌握多线程安全队列) BlockingCollection  阻塞集合使用教程 多线程安全集合 生产者消费者模式 第1张

什么是 BlockingCollection?

BlockingCollection<T> 是 .NET Framework 4.0 引入的一个线程安全集合类,位于 System.Collections.Concurrent 命名空间。它本质上是对 IProducerConsumerCollection<T>(如 ConcurrentQueue<T>ConcurrentStack<T> 等)的封装,并提供了阻塞和限界(bounded capacity)功能。

主要特点包括:

  • 线程安全:多个线程可同时添加或取出元素而不会引发异常。
  • 阻塞操作:当集合为空时,取操作会阻塞直到有新元素加入;当集合满时(如果设置了容量上限),添加操作会阻塞直到有空间可用。
  • 支持完成通知:通过 CompleteAdding() 方法通知消费者不再有新数据。

基本使用方法

下面是一个简单的 生产者-消费者 示例,展示如何使用 BlockingCollection<int>

using System;using System.Collections.Concurrent;using System.Threading;using System.Threading.Tasks;class Program{    static void Main()    {        // 创建一个无容量上限的 BlockingCollection,默认底层使用 ConcurrentQueue        var blockingCollection = new BlockingCollection<int>();        // 启动生产者任务        Task producer = Task.Run(() =>        {            for (int i = 1; i <= 5; i++)            {                blockingCollection.Add(i);                Console.WriteLine($"生产者:添加了 {i}");                Thread.Sleep(500); // 模拟耗时操作            }            // 标记添加完成            blockingCollection.CompleteAdding();            Console.WriteLine("生产者:完成添加。");        });        // 启动消费者任务        Task consumer = Task.Run(() =>        {            // 使用 GetConsumingEnumerable() 安全遍历,直到 CompleteAdding 被调用            foreach (int item in blockingCollection.GetConsumingEnumerable())            {                Console.WriteLine($"消费者:处理了 {item}");                Thread.Sleep(800); // 模拟处理时间            }            Console.WriteLine("消费者:处理完毕。");        });        // 等待两个任务完成        Task.WaitAll(producer, consumer);        Console.WriteLine("程序结束。");    }}

运行上述代码,你会看到生产者逐个添加数字,消费者逐个处理,即使两者速度不同,也能协调工作。这正是 多线程安全集合 的强大之处。

设置容量上限(Bounded Capacity)

有时我们希望限制集合的最大容量,防止内存溢出。可以通过构造函数指定容量上限:

// 创建一个最多容纳 3 个元素的 BlockingCollectionvar boundedCollection = new BlockingCollection<string>(3);// 如果尝试添加第4个元素,Add() 会阻塞,直到有元素被取走boundedCollection.Add("A");boundedCollection.Add("B");boundedCollection.Add("C");// 此时再 Add 会等待boundedCollection.Add("D"); // 阻塞,直到消费者取走一个

常用方法总结

方法 说明
Add(T item) 向集合添加元素(若已满则阻塞)
TryAdd(T item, TimeSpan timeout) 尝试在指定时间内添加,超时返回 false
Take() 取出并移除一个元素(若为空则阻塞)
TryTake(out T item, TimeSpan timeout) 尝试在指定时间内取出,超时返回 false
GetConsumingEnumerable() 返回一个可枚举对象,自动阻塞并消费元素,直到调用 CompleteAdding()
CompleteAdding() 标记集合不再接受新元素,通知消费者即将结束

适用场景

BlockingCollection 非常适合以下场景:

  • 生产者-消费者模式:如日志收集、任务分发、消息队列等。
  • 需要线程安全且自动协调生产/消费速度的场合。
  • 希望避免手动使用锁(lock)来同步集合访问的复杂性。

小结

通过本文,你应该已经掌握了 C# BlockingCollection 的基本用法。它不仅简化了多线程编程中的数据传递逻辑,还通过阻塞机制天然实现了线程间的协调。无论是构建高性能服务器、后台任务处理系统,还是简单的并发应用,阻塞集合使用教程 中介绍的技术都能为你提供强大支持。

记住关键点:使用 GetConsumingEnumerable() + CompleteAdding() 是最推荐的消费方式;合理设置容量上限可以防止资源耗尽;所有操作都是线程安全的,无需额外加锁。

现在,你已经准备好在自己的项目中使用 多线程安全集合 来构建高效、稳定的并发程序了!