0

0

.NET多线程编程—并发集合

黄舟

黄舟

发布时间:2017-02-06 14:29:51

|

1378人浏览过

|

来源于php中文网

原创

并发集合

1 为什么使用并发集合?

原因主要有以下几点:

  • System.Collections和System.Collections.Generic名称空间中所提供的经典列表、集合和数组都不是线程安全的,若无同步机制,他们不适合于接受并发的指令来添加和删除元素。

  • 在并发代码中使用上述经典集合需要复杂的同步管理,使用起来很不方便。

  • 使用复杂的同步机制会大大降低性能。

  • NET Framework 4所提供的新的集合尽可能地减少需要使用锁的次数。这些新的集合通过使用比较并交换(compare-and-swap,CAS)指令和内存屏障,避免使用互斥的重量级锁。这对性能有保障。

注意:与经典集合相比,并发集合会有更大的开销,因此在串行代码中使用并发集合无意义,只会增加额外的开销且运行速度比访问经典集合慢。

2 并发集合

1)ConcurrentQueue:线程安全的先进先出 (FIFO) 集合

主要方法:

  • Enqueue(T item);将对象添加到集合结尾。

  • TryDequeue(out T result); 尝试移除并返回位于集合开始处的对象,返回值表示操作是否成功。

  • TryPeek(out T result);尝试返回集合开始处的对象,但不将其移除,返回值表示操作是否成功。

说明:

  • ConcurrentQueue是完全无锁的,但当CAS操作失败且面临资源争用时,它可能会自旋并且重试操作。

  • ConcurrentQueue是FIFO集合,某些和出入顺序无关的场合,尽量不要用ConcurrentQueue。

2)ConcurrentStack:线程安全的后进先出 (LIFO) 集合

主要方法及属性:

  • Push(T item);将对象插入集合的顶部。

  • TryPop(out T result);尝试弹出并返回集合顶部的对象,返回值表示操作是否成功。

  • TryPeek(out T result);尝试返回集合开始处的对象,但不将其移除,返回值表示操作是否成功。

  • IsEmpty { get; }指示集合是否为空。

  • PushRange(T[] items);将多个对象插入集合的顶部。

  • TryPopRange(T[] items);弹出顶部多个元素,返回结果为弹出元素个数。

说明:

  • 与ConcurrentQueue相似地,ConcurrentStack完全无锁的,但当CAS操作失败且面临资源争用时,它可能会自旋并且重试操作。

  • 获取集合是否包含元素使用IsEmpty属性,而不是通过判断Count属性是否大于零。调用Count比调用IsEmpty开销大。

  • 使用PushRange(T[] items)和TryPopRange(T[] items)时注意缓冲引起的额外开销和额外的内存消耗。

3) ConcurrentBag:元素可重复的无序集合

主要方法及属性:

  • TryPeek(out T result);尝试从集合返回一个对象,但不移除该对象,返回值表示是否成功获得该对象。

  • TryTake(out T result);尝试从集合返回一个对象并移除该对象,返回值表示是否成功获得该对象。

  • Add(T item);将对象添加到集合中。

  • IsEmpty { get; }解释同ConcurrentStack

说明:

  • ConcurrentBag为每一个访问集合的线程维护了一个本地队列,在可能的情况下,它会以无锁的方式访问本地队列。

  • ConcurrentBag在同一个线程添加和删除元素的场合下效率非常高。

  • 因为ConcurrentBag有时会需要锁,在生产者线程和消费者线程完全分开的场景下效率非常低。

  • ConcurrentBag调用IsEmpty的开销非常大,因为这需要临时获得这个无序组的所有锁。

4)BlockingCollection:实现

System.Collections.Concurrent.IProducerConsumerCollection 的线程安全集合,提供阻塞和限制功能

主要方法及属性:

  • BlockingCollection(int boundedCapacity);boundedCapacity表示集合限制大小。

  • CompleteAdding();将BlockingCollection实例标记为不再接受任何添加。

    去日租网站系统
    去日租网站系统

    去日租程序是一款具有强大的功能的基于.NET+SQL2000+AJAX构架的房屋出租管理系统。 日租网站管理系统,采用ASP.NET2.0语言开发,它集成租房模块、文章模块、订单模块、邮箱短信模块、用户模板、SEO优化模块、房间模块、支付模块等多项强大功能。系统有多年经验的高级工程师采用三层架构开发,页面代码全部采用DIV+CSS,完全符合SEO标准,有利于搜索引擎关键排名优化。日租网站

    下载
  • IsCompleted { get; }此集合是否已标记为已完成添加并且为空。

  • GetConsumingEnumerable();从集合中移除并返回移除的元素

  • Add(T item);添加元素到集合。

  • TryTake(T item, int millisecondsTimeout, CancellationToken cancellationToken);

说明:

  • 使用BlockingCollection()构造函数实例化BlockingCollection,意味着不设置boundedCapacity,那么boundedCapacity为默认值: int.MaxValue。

  • 限界:使用BlockingCollection(int boundedCapacity),设置boundedCapacity的值,当集合容量达到这个值得时候,向BlockingCollection添加元素的线程将会被阻塞,直到有元素被删除。

限界功能可控制内存中集合最大大小,这对于需要处理大量元素的时候非常有用。

  • 默认情况下,BlockingCollection封装了一个ConcurrentQueue。可以在构造函数中指定一个实现了IProducerConsumerCollection接口的并发集合,包括:ConcurrentStack、ConcurrentBag。

  • 使用此集合包含易于无限制等待的风险,所以使用TryTake更加,因为TryTake提供了超时控制,指定的时间内可以从集合中移除某个项,则为 true;否则为 false。

5)ConcurrentDictionary:可由多个线程同时访问的键值对的线程安全集合。

主要方法

AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory);如果指定的键尚不存在,则将键/值对添加到 字典中;如果指定的键已存在,则更新字典中的键/值对。

  • GetOrAdd(TKey key, TValue value);如果指定的键尚不存在,则将键/值对添加到字典中。

  • TryRemove(TKey key, out TValue value);尝试从字典中移除并返回具有指定键的值。

  • TryUpdate(TKey key, TValue newValue, TValue comparisonValue);将指定键的现有值与指定值进行比较,如果相等,则用第三个值更新该键。

说明:

  • ConcurrentDictionary对于读操作是完全无锁的。当多个任务或线程向其中添加元素或修改数据的时候,ConcurrentDictionary使用细粒度的锁。使用细粒度的锁只会锁定真正需要锁定的部分,而不是整个字典。

6)IProducerConsumerCollection:定义供生产者/消费者用来操作线程安全集合的方法。 此接口提供一个统一的表示(为生产者/消费者集合),从而更高级别抽象如 System.Collections.Concurrent.BlockingCollection可以使用集合作为基础的存储机制。

3.常用模式

1)并行的生产者-消费者模式

定义:

生成者和消费者是此模式中的两类对象模型,消费者依赖于生产者的结果,生产者生成结果的同时,消费者使用结果。

894.jpg

图1 并行的生产者-消费者模式

说明:

  • 并发集合用在此模式下非常合适,因为并发集合支持此模式中对象的并行操作。

  • 若不使用并发集合,那么就要加入同步机制,从而使程序变得比较复杂,难于维护和理解,同时大大降低性能。

  • 上图为生产者消费者模式示意图,纵轴为时间轴,生成者与消费者的并不在一条时间线上,但二者有交叉,意在表明生成者先产生结果,而后消费者才真正使用了生成者产生的数据。

2)流水线模式

定义:

流水线由多个阶段构成,每个阶段由一系列的生产者和消费者构成。一般来讲前一个阶段是后一个阶段的生成者;依靠相邻两个阶段之间的缓冲区队列,每个阶段可以并发执行。

895.jpg

图2 并行的流水线模式

说明:

  • 常使用BlockingCollection作为缓冲罐区队列。

  • 流水线的速度近似等于流水线最慢阶段的速度。

  • 上图为流水线模式示意图,前一阶段为后一阶段的生成者,这里展示了最为简单和基本的流水线模式,更复杂的模式可以认为是每个阶段都包括了对数据更多的处理过程。

4 使用方式

仅以ConcurrentBag和BlockingCollection为例,其他的并发集合与之相似。

ConcurrentBag

List list = ......
ConcurrentBag bags = new ConcurrentBag();
Parallel.ForEach(list, (item) => 
{
    //对list中的每个元素进行处理然后,加入bags中
    bags.Add(itemAfter);
});

BlockingCollection—生产者消费者模式

public static void Execute()
{
            //调用Invoke,使得生产者任务和消费者任务并行执行
            //Producer方法和Customer方法在Invoke中的参数顺序任意,不论何种顺序都会获得正确的结果
            Parallel.Invoke(()=>Customer(),()=>Producer());
            Console.WriteLine(string.Join(",",customerColl));
}
//生产者集合
private static BlockingCollection producerColl = new BlockingCollection();
 //消费者集合
private static BlockingCollection customerColl = new BlockingCollection();
public static void Producer()
{
            //循环将数据加入生成者集合
            for (int i = 0; i < 100; i++)
            {
                producerColl.Add(i);
            }
            //设置信号,表明不在向生产者集合中加入新数据
            //可以设置更加复杂的通知形式,比如数据量达到一定值且其中的数据满足某一条件时就设置完成添加
            producerColl.CompleteAdding();
}
public static void Customer()
{
            //调用IsCompleted方法,判断生产者集合是否在添加数据,是否还有未"消费"的数据
            //注意不要使用IsAddingCompleted,IsAddingCompleted只表明集合标记为已完成添加,而不能说明其为空
            //而IsCompleted为ture时,那么IsAddingCompleted为ture且集合为空
            while (!producerColl.IsCompleted)
            {
                //调用Take或TryTake "消费"数据,消费一个,移除一个
                //TryAdd的好处是提供超时机制
                customerColl.Add(string.Format("消费:{0}", producerColl.Take()));
            }
}

以上就是.NET多线程编程—并发集合的内容,更多相关内容请关注PHP中文网(www.php.cn)!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

2

2026.01.29

java配置环境变量教程合集
java配置环境变量教程合集

本专题整合了java配置环境变量设置、步骤、安装jdk、避免冲突等等相关内容,阅读专题下面的文章了解更多详细操作。

2

2026.01.29

java成品学习网站推荐大全
java成品学习网站推荐大全

本专题整合了java成品网站、在线成品网站源码、源码入口等等相关内容,阅读专题下面的文章了解更多详细推荐内容。

0

2026.01.29

Java字符串处理使用教程合集
Java字符串处理使用教程合集

本专题整合了Java字符串截取、处理、使用、实战等等教程内容,阅读专题下面的文章了解详细操作教程。

0

2026.01.29

Java空对象相关教程合集
Java空对象相关教程合集

本专题整合了Java空对象相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.29

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

25

2026.01.29

clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址
clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址

clawdbot龙虾机器人官网入口:https://clawd.bot/,clawdbot ai是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

16

2026.01.29

Golang 网络安全与加密实战
Golang 网络安全与加密实战

本专题系统讲解 Golang 在网络安全与加密技术中的应用,包括对称加密与非对称加密(AES、RSA)、哈希与数字签名、JWT身份认证、SSL/TLS 安全通信、常见网络攻击防范(如SQL注入、XSS、CSRF)及其防护措施。通过实战案例,帮助学习者掌握 如何使用 Go 语言保障网络通信的安全性,保护用户数据与隐私。

8

2026.01.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

622

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 7.9万人学习

python编程入门系列图文教程
python编程入门系列图文教程

共65课时 | 24.7万人学习

vscode其实很简单
vscode其实很简单

共72课时 | 29.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号