C# BlockingCollection
- 什么是 `BlockingCollection<T>`
- 主要特点
- 构造函数
- 常用方法
- 生产者操作
- 消费者操作
- 示例代码
- 注意事项
- 串口接收
- 底层存储的类型
- 线程安全和并发访问
- 串口数据接收的顺序性
- 关键点
BlockingCollection<T>
是
C#
中一个非常有用的线程安全集合类,位于
System.Collections.Concurrent
命名空间中。它主要用于在多线程环境中实现
线程安全的生产者-消费者模式。
以下是关于
BlockingCollection<T>
的详细介绍:
什么是 BlockingCollection<T>
BlockingCollection<T>
是一个线程安全的集合,它提供了一种机制,允许一个或多个生产者线程将数据添加到集合中,同时允许一个或多个消费者线程从集合中取出数据。它内部封装了一个线程安全的集合(如 ConcurrentQueue<T>
、ConcurrentStack<T>
或 ConcurrentBag<T>
),并提供了阻塞和限制集合大小的功能。
主要特点
- 线程安全:内部使用锁或其他同步机制,确保在多线程环境下对集合的操作是安全的。
- 阻塞操作:当集合为空时,消费者线程会阻塞等待,直到有数据可用;当集合达到最大容量时,生产者线程会阻塞等待,直到有空间可用。
- 限制大小:可以通过构造函数指定集合的最大容量。
- 支持多种底层集合:可以使用
ConcurrentQueue<T>
(默认)、ConcurrentStack<T>
或ConcurrentBag<T>
作为底层存储结构。
构造函数
BlockingCollection<T>
提供了多种构造方式:
// 使用默认的 ConcurrentQueue<T>,无容量限制
var blockingCollection = new BlockingCollection<int>();// 使用默认的 ConcurrentQueue<T>,并指定最大容量
var blockingCollection = new BlockingCollection<int>(10);// 指定底层集合类型
var blockingCollection = new BlockingCollection<int>(new ConcurrentStack<int>());
常用方法
生产者操作
Add(T item)
:将一个元素添加到集合中。如果集合已满,会抛出异常。
TryAdd(T item)
:尝试将一个元素添加到集合中。如果集合已满,返回 false
。
TryAdd(T item, TimeSpan timeout)
:尝试在指定的超时时间内将元素添加到集合中。
CompleteAdding()
:标记集合不再添加新的元素。消费者线程在集合为空时会收到通知并退出。
消费者操作
Take()
:从集合中取出一个元素。如果集合为空,线程会阻塞等待。
TryTake(out T item)
:尝试从集合中取出一个元素。如果集合为空,返回 false
。
TryTake(out T item, TimeSpan timeout)
:尝试在指定的超时时间内从集合中取出一个元素。
GetConsumingEnumerable()
:返回一个可枚举的集合,消费者可以使用 foreach
遍历集合中的元素。当调用 CompleteAdding()
后,枚举会结束。
示例代码
以下是一个简单的生产者-消费者示例,使用 BlockingCollection<T>
实现:
using System;
using System.Collections.Concurrent;
using System.Threading;class Program
{static void Main(){// 创建一个容量为 5 的 BlockingCollectionvar blockingCollection = new BlockingCollection<int>(5);// 启动生产者线程Thread producerThread = new Thread(() =>{for (int i = 1; i <= 10; i++){blockingCollection.Add(i); // 添加元素Console.WriteLine($"Producer added: {i}");Thread.Sleep(500); // 模拟生产时间}blockingCollection.CompleteAdding(); // 标记不再添加元素});// 启动消费者线程Thread consumerThread = new Thread(() =>{foreach (var item in blockingCollection.GetConsumingEnumerable()){Console.WriteLine($"Consumer consumed: {item}");Thread.Sleep(1000); // 模拟消费时间}});producerThread.Start();consumerThread.Start();producerThread.Join();consumerThread.Join();}
}
输出示例
Producer added: 1
Producer added: 2
Consumer consumed: 1
Producer added: 3
Consumer consumed: 2
Producer added: 4
Consumer consumed: 3
Producer added: 5
Consumer consumed: 4
Producer added: 6
Consumer consumed: 5
Producer added: 7
Consumer consumed: 6
Producer added: 8
Consumer consumed: 7
Producer added: 9
Consumer consumed: 8
Producer added: 10
Consumer consumed: 9
Consumer consumed: 10
注意事项
- 线程安全:
BlockingCollection<T>
是线程安全的,但需要确保对集合的操作不会与其他非线程安全的操作混用。 - 容量限制:如果集合满了,生产者线程会阻塞,因此需要合理设置容量。
- 异常处理:在生产者调用
Add()
或消费者调用Take()
时,可能会抛出异常(如集合已满或已标记为完成添加)。建议使用TryAdd()
和TryTake()
方法来避免异常。
BlockingCollection<T>
是C#
中实现线程安全的生产者-消费者模式的利器,它简化了线程同步的复杂性,非常适合多线程编程场景。
串口接收
在使用 BlockingCollection<T>
存储串口接收的数据,并在其他线程中取出时,是否能保证数据的顺序,主要取决于以下两个因素:
底层存储的类型
BlockingCollection<T>
允许指定底层存储的类型。默认情况下,它使用 ConcurrentQueue<T>
作为底层存储,而 ConcurrentQueue<T>
是一个先进先出 FIFO的队列。这意味着数据的添加顺序和取出顺序是一致的,因此可以保证顺序。
如果你使用其他类型的底层存储(如 ConcurrentStack<T>
或自定义的线程安全集合),则顺序可能会有所不同。例如:
ConcurrentQueue<T>
:保证 FIFO 顺序。
ConcurrentStack<T>
:保证 LIFO(后进先出)顺序。
线程安全和并发访问
BlockingCollection<T>
是线程安全的,因此即使在多线程环境下,数据的添加和取出操作也是安全的。只要底层存储是 FIFO 的(如 ConcurrentQueue<T>
),数据的顺序就能得到保证。
串口数据接收的顺序性
串口通信本身是按字节顺序接收数据的,因此只要数据是逐字节接收并立即添加到 BlockingCollection<T>
中,数据的顺序就能得到保证。
示例代码
以下是一个示例,展示如何使用 BlockingCollection<T>
存储串口接收的数据,并在其他线程中按顺序取出:
using System;
using System.Collections.Concurrent;
using System.IO.Ports;
using System.Threading;class SerialPortExample
{private SerialPort _serialPort;private BlockingCollection<string> _dataQueue = new BlockingCollection<string>();public SerialPortExample(string portName){_serialPort = new SerialPort(portName){BaudRate = 9600,DataBits = 8,Parity = Parity.None,StopBits = StopBits.One,ReadTimeout = 500};_serialPort.DataReceived += SerialPort_DataReceived;}private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){try{string data = _serialPort.ReadLine(); // 假设数据以换行符分隔_dataQueue.Add(data); // 将数据添加到阻塞集合Console.WriteLine($"Received and added: {data}");}catch (Exception ex){Console.WriteLine($"Error in DataReceived: {ex.Message}");}}public void Start(){_serialPort.Open();Thread consumerThread = new Thread(ConsumeData);consumerThread.Start();}private void ConsumeData(){foreach (var data in _dataQueue.GetConsumingEnumerable()){Console.WriteLine($"Consumed: {data}");// 处理数据}}public void Stop(){_dataQueue.CompleteAdding();_serialPort.Close();}static void Main(){SerialPortExample example = new SerialPortExample("COM3");example.Start();Console.WriteLine("Press Enter to exit...");Console.ReadLine();example.Stop();}
}
关键点
- 底层存储:使用
ConcurrentQueue<T>
(默认)可以保证数据的 FIFO 顺序。 - 线程安全:
BlockingCollection<T>
是线程安全的,因此在多线程环境下不会出现数据顺序混乱的问题。 - 串口数据接收:只要串口接收的数据是按顺序添加到
BlockingCollection<T>
中的,顺序就能得到保证。
因此,只要使用默认的 ConcurrentQueue<T>
作为底层存储,并且正确处理串口数据的接收和添加,BlockingCollection<T>
是可以保证数据顺序的。
相关文章:

C# BlockingCollection
什么是 BlockingCollection<T>主要特点构造函数常用方法生产者操作消费者操作 示例代码注意事项串口接收底层存储的类型线程安全和并发访问串口数据接收的顺序性关键点 BlockingCollection<T> 是 C# 中一个非常有用的线程安全集合类,位于 System.Coll…...

学习笔记11——并发编程之并发关键字
并发关键字 synchronized关键字 在应用Sychronized关键字时需要把握如下注意点: 1.一把锁只能同时被一个线程获取,没有获得锁的线程只能等待; 2.每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁…...

2.2 Windows本地部署DeepSeek模型 --- Ollama篇(下)
2.3Ollama加载已下载Deepseek模型 无网络连接,直接通过Ollama本地已经本地已经下载好的的Deepseek模型。 2.3.1 查看已安装模型 PS C:\Users\Administrator> ollama list NAME ID SIZE MODIFIED deepseek-r1:8…...

DeepSeek R1-32B医疗大模型的完整微调实战分析(全码版)
DeepSeek R1-32B微调实战指南 ├── 1. 环境准备 │ ├── 1.1 硬件配置 │ │ ├─ 全参数微调:4*A100 80GB │ │ └─ LoRA微调:单卡24GB │ ├── 1.2 软件依赖 │ │ ├─ PyTorch 2.1.2+CUDA │ │ └─ Unsloth/ColossalAI │ └── 1.3 模…...

mysql的锁--一篇读懂所有锁机制
目录 mysql的锁 概述:根据mysql锁的大类型可以分为 我们先来讲一下范围最大的全局锁 使用 为什么要使用全局锁? 使用全局锁进行备份的缺点 表级锁 表锁 1.共享读表锁的语法 2.排斥写表锁 元数据锁 意向锁 什么是意向锁 怎么产生意向锁 意向…...

LTC6804、LTC6811、LTC6813的使用
FSEC自制BMS第一步:从零开发使用LTC6804采集电池电压 LTC6811特性 LTC6811 是 LTC6804 的引脚兼容型升级器件,LTC6804官方已经不推荐选用 可测量多达 12 节串联电池 1.2mV 最大总测量误差 可堆叠式架构能支持几百个电池 内置 isoSPI™ 接口 可在 290μ…...

linux内存页块划分及位图存储机制
page_alloc.c - mm/page_alloc.c - Linux source code v5.4.285 - Bootlin Elixir Cross Referencer 一. 什么是页块(Pageblock)? 定义:页块是物理内存中的一个连续区域,由 2^pageblock_order 个物理页(Pag…...

Vue 文件下载功能的跨域处理与前后端实现详解
在 Web 应用开发中,文件下载功能是常见需求。但由于跨域限制和认证机制的复杂性,实际开发中常遇到下载失败或权限错误等问题。本文将结合 Vue 前端和 Spring Boot 后端,详细介绍文件下载功能的实现与跨域问题的解决方案。 一、问题背景 在某…...

boost::beast websocket 实例
环境:ubuntu 1. 安装boost sudo apt install -y libboost-all-dev 2. Server端 #include <boost/asio.hpp> #include <boost/beast.hpp> #include <iostream> #include <thread>namespace beast boost::beast; // 从 Boost.Beast 中导…...

复试难度,西电卓越工程师学院(杭研院)考研录取情况
01、卓越工程师学院各个方向 02、24卓越工程师学院(杭研院)近三年复试分数线对比 PS:卓越工程师学院分为广研院、杭研院 分别有新一代电子信息技术、通信工程、集成电路工程、计算机技术、光学信息工程、网络信息安全、机械,这些…...

Rabbitmq--延迟消息
13.延迟消息 延迟消息:生产者发送消息时指定一个时间,消费者不会立刻收到消息,而是在指定时间之后才会收到消息 延迟任务:一定时间之后才会执行的任务 1.死信交换机 当一个队列中的某条消息满足下列情况之一时,就会…...

cocos creator使用mesh修改图片为圆形,减少使用mask,j减少drawcall,优化性能
cocos creator版本2.4.11 一个mask占用drawcall 3个以上,针对游戏中技能图标,cd,以及多玩家头像,是有很大优化空间 1.上代码,只适合单独图片的,不适合在图集中的图片 const { ccclass, property } cc._decorator;c…...

C++ Qt开发成长之路,从入门到企业级实战项目,保姆级学习路线
Qt 介绍 Qt是一个跨平台的C图形用户界面应用程序开发框架,最初由挪威的Trolltech公司开发,后来被诺基亚收购,现在由Qt公司维护。它提供了丰富的工具和类库,使开发者能够轻松地创建各种类型的应用程序,包括桌面应用、移…...

JavaWeb后端基础(7)AOP
AOP是Spring框架的核心之一,那什么是AOP?AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。AOP是一种思想,而在Spring框…...

Uniapp实现地图获取定位功能
摘要:本文将手把手教你如何在Uniapp项目中集成地图功能、实现定位获取,并解决微信小程序、APP、H5三端的兼容性问题🚀🚀🚀 一、环境准备 地图平台选择 微信小程序:腾讯地图(强制使用)…...

批量将 Excel 转换 PDF/Word/CSV以及图片等其它格式
Excel 格式转换是我们工作过程当中非常常见的一个需求,我们通常需要将 Excel 转换为其他各种各样的格式。比如将 Excel 转换为 PDF、比如说将 Excel 转换为 Word、再比如说将 Excel文档转换为图片等等。 这些操作对我们来讲都不难,因为我们通过 Office 都…...
Flutter:StatelessWidget vs StatefulWidget 深度解析
目录 1. 引言 2. StatelessWidget(无状态组件) 2.1 定义与特点 2.2 代码示例 3. StatefulWidget(有状态组件) 3.1 定义与特点 3.2 代码示例 4. StatelessWidget vs StatefulWidget 对比 5. StatefulWidget 生命周期 5.1…...

Stream流学习
Stream流 把数据放进stream流水线,对数据进行一系列操作(中间方法),最后封装(终结方法)。 Stream.of()允许传入任何参数 常见中间方法 可以对数据进行链式(流水线)操作,但…...

多视图几何--恢复相机位姿/内参的几种方法
恢复相机位姿的几种方法 1分解投影矩阵 1.1投影矩阵分解为相机内外参矩阵的完整解析 投影矩阵(Projection Matrix)是计算机视觉中将三维世界点映射到二维像素坐标的核心工具,其本质是相机内参矩阵(Intrinsic Matrix)…...

[数据结构]堆详解
目录 一、堆的概念及结构 二、堆的实现 1.堆的定义 2堆的初始化 3堆的插入 编辑 4.堆的删除 5堆的其他操作 6代码合集 三、堆的应用 (一)堆排序(重点) (二)TOP-K问题 一、堆的概念及结构 堆的…...

领域驱动设计(DDD)与MVC架构:理念对比与架构选择
领域驱动设计(DDD)与MVC架构:理念对比与架构选择 一、架构之争的本质:业务复杂度驱动技术演进 在软件开发领域,没有银弹式的完美架构,只有适合当前业务场景的合理选择。MVC与DDD的区别本质上是业务复杂度与…...

牛客周赛:84:B:JAVA
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner scanner new Scanner(S…...

【理想解法学习笔记】
目录 理想解法原理简介算法步骤属性值规范化方法代码示例 理想解法 原理简介 TOPSIS(Technique for Order Preference by Simi larity to IdealSolution)法是一种逼近理想解的排序方法。其基本的处理思路是:首先建立初始化决策矩阵,而后基于规范化后的初…...

CI/CD—Jenkins配置一次完整的jar自动化发布流程
背景: 实现设想: 要创建自动化发布,需要准备一台测试服务器提前安装好java运行所需的环境,JDK版本最好和Windows开发机器上的版本一致,在Jenkins上配置将构建好的jar上传到测试服务器上,测试服务器自动启动…...

Magento2根据图片文件包导入产品图片
图片包给的图片文件是子产品的图片,如下图:A104255是主产品的sku <?php/*** 根据图片包导入产品图片,包含子产品和主产品* 子产品是作为主图,主产品是作为附加图片*/use Magento\Framework\App\Bootstrap;include(../app/boot…...

从零开始的python学习(五)P71+P72+P73+P74
本文章记录观看B站python教程学习笔记和实践感悟,视频链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…...

OpenHarmony5.0分布式系统源码实现分析—软总线
一、引言 OpenHarmony 作为一款面向万物互联的操作系统,其分布式软总线(Distributed SoftBus)是实现设备间高效通信和协同的核心技术之一。分布式软总线通过构建一个虚拟的总线网络,使得不同设备能够无缝连接、通信和协同工作。本…...

基于SpringBoot实现旅游酒店平台功能六
一、前言介绍: 1.1 项目摘要 随着社会的快速发展和人民生活水平的不断提高,旅游已经成为人们休闲娱乐的重要方式之一。人们越来越注重生活的品质和精神文化的追求,旅游需求呈现出爆发式增长。这种增长不仅体现在旅游人数的增加上࿰…...

代码随想录算法训练营第六十一天 | 108. 冗余连接 109. 冗余连接II
108. 冗余连接 题目链接:KamaCoder 文档讲解:代码随想录 状态:AC Java代码: import java.util.*;class Main {public static int[] father;public static void main(String[] args) {Scanner scan new Scanner(System.in);int n…...

RoboVQA:机器人多模态长范围推理
23 年 11 月来自 Google Deepmind 的论文“RoboVQA: Multimodal Long-Horizon Reasoning for Robotics”。 本文提出一种可扩展、自下而上且本质多样化的数据收集方案,该方案可用于长期和中期的高级推理,与传统的狭窄自上而下的逐步收集相比,…...