编辑
2025-09-18
C#
00

目录

特点
1. 线程安全
2. 阻塞和非阻塞支持
3. 有界和无界
4. 支持多种底层集合
5. 完成添加操作的通知
6. 使用模式灵活
BlockingCollection基本用法
示例1:简单的生产者-消费者模式
完整的WinForm示例

BlockingCollection<T>是.NET Framework中的一个线程安全集合,它提供了阻塞和限制功能。这意味着如果集合为空,尝试从集合中取出元素的操作将会阻塞,直到有元素可以取出为止;如果集合已达到设定的容量上限,尝试添加元素的操作也将会阻塞,直到集合中有空间为止。这使得BlockingCollection<T>非常适合用于生产者-消费者场景。

特点

BlockingCollection<T> 是 .NET Framework 中提供的一个集合类,专为并发场景设计,以支持多线程的生产者-消费者模式。这个集合的特点使其成为在多线程应用程序中进行数据共享和处理的理想选择。以下是 BlockingCollection<T> 的一些关键特点:

1. 线程安全

BlockingCollection<T> 内部采用适当的锁机制,确保在多线程环境下的操作(如添加或移除元素)是线程安全的。这意味着开发者无需担心多线程并发访问和修改集合所带来的竞争条件或数据不一致的问题。

2. 阻塞和非阻塞支持

BlockingCollection<T> 提供了阻塞和非阻塞的数据访问方法。例如,Add 方法用于添加元素,而 Take 方法在尝试取出元素时,如果集合为空,则会阻塞调用线程直到集合中有元素可取。此外,还提供了 TryAddTryTake 方法,这些方法在无法立即执行时不会阻塞调用线程,而是返回一个布尔值指示操作是否成功。

3. 有界和无界

BlockingCollection<T> 可以是有界的也可以是无界的。在创建集合时,可以指定一个容量上限来创建有界集合,这意味着当集合达到其容量上限时,尝试添加更多元素的操作将会阻塞,直到集合中有空间为止。如果不指定容量上限,则集合为无界的,理论上可以无限制地添加元素(受系统内存限制)。

4. 支持多种底层集合

虽然 BlockingCollection<T> 本身提供了一套丰富的API,但它实际上是一个包装器,可以包装不同的线程安全集合,如 ConcurrentQueue<T>ConcurrentStack<T>ConcurrentBag<T>。这意味着你可以根据需要选择不同的数据结构作为底层存储,以支持不同的使用场景。

5. 完成添加操作的通知

BlockingCollection<T> 提供了 CompleteAdding 方法,允许生产者通知消费者不会再有更多的元素被添加到集合中。这对于确保消费者可以正确地完成处理并退出消费循环是非常有用的。

6. 使用模式灵活

BlockingCollection<T> 支持多个生产者和多个消费者的场景,这使得它非常适合复杂的并行处理和数据流动任务。

BlockingCollection基本用法

示例1:简单的生产者-消费者模式

下面是一个简单的生产者-消费者模式的示例,其中生产者将整数添加到BlockingCollection中,消费者则从中取出这些整数。

C#
using System; using System.Collections.Concurrent; using System.Threading.Tasks; class Program { static void Main(string[] args) { BlockingCollection<int> collection = new BlockingCollection<int>(boundedCapacity: 5); // 生产者任务 var producer = Task.Run(() => { for (int i = 0; i < 10; i++) { collection.Add(i); Console.WriteLine($"生产: {i}"); } collection.CompleteAdding(); }); // 消费者任务 var consumer = Task.Run(() => { foreach (var item in collection.GetConsumingEnumerable()) { Console.WriteLine($"消费: {item}"); } }); Task.WaitAll(producer, consumer); } }

image.png

在这个示例中,我们创建了一个BlockingCollection<int>,其容量限制为5。然后,我们启动了一个生产者任务,它向集合中添加整数,以及一个消费者任务,它从集合中取出并打印这些整数。当生产者完成添加操作后,我们调用CompleteAdding方法来通知消费者不会有更多的项被添加到集合中。

完整的WinForm示例

下面是一个使用WinForms实现的生产者-消费者示例。在这个示例中,我们将创建一个简单的界面,其中包含用于启动和停止生产者和消费者任务的按钮,以及显示信息的文本框。

首先,创建一个新的WinForms应用程序,并添加以下控件:

  • 两个Button控件,分别命名为startButtonstopButton
  • 一个TextBox控件,命名为logTextBox,将其Multiline属性设置为True以显示多行文本。

然后,添加以下代码:

C#
using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace BlockingCollectionExample { public partial class MainForm : Form { private BlockingCollection<int> _collection = new BlockingCollection<int>(5); private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private Task _producerTask; private Task _consumerTask; public MainForm() { InitializeComponent(); } private void startButton_Click(object sender, EventArgs e) { _cancellationTokenSource = new CancellationTokenSource(); CancellationToken ct = _cancellationTokenSource.Token; // 生产者任务 _producerTask = Task.Run(() => { try { for (int i = 0; !ct.IsCancellationRequested; i++) { _collection.Add(i); this.Invoke((Action)(() => { logTextBox.AppendText($"生产: {i}\r\n"); })); Thread.Sleep(500); // 模拟生产耗时 } } catch (OperationCanceledException) { _collection.CompleteAdding(); } }, ct); // 消费者任务 _consumerTask = Task.Run(() => { try { foreach (var item in _collection.GetConsumingEnumerable(ct)) { this.Invoke((Action)(() => { logTextBox.AppendText($"消费: {item}\r\n"); })); Thread.Sleep(1000); // 模拟消费耗时 } } catch (OperationCanceledException) { } }, ct); } private void stopButton_Click(object sender, EventArgs e) { _cancellationTokenSource.Cancel(); try { Task.WaitAll(new[] { _producerTask, _consumerTask }); } catch (AggregateException) { } _collection = new BlockingCollection<int>(5); // 重置集合 } } }

image.png

在这个WinForm应用程序中,当用户点击startButton时,将启动生产者和消费者任务。生产者将整数添加到BlockingCollection中,而消费者则从中取出这些整数并显示在logTextBox中。点击stopButton将取消这些任务并重置集合,以便可以重新开始。

这个示例展示了如何在WinForms应用程序中使用BlockingCollection来实现线程安全的生产者-消费者模式,同时也展示了如何使用CancellationToken来优雅地取消任务。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!