首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >.NET(C#):线程安全集合的阻塞BlockingCollection的使用

.NET(C#):线程安全集合的阻塞BlockingCollection的使用

作者头像
zls365
发布于 2021-01-13 07:53:02
发布于 2021-01-13 07:53:02
1.9K00
代码可运行
举报
文章被收录于专栏:CSharp编程大全CSharp编程大全
运行总次数:0
代码可运行

目录

  • 1. 限制最大容量:BoundedCapacity
  • 2. 禁止加入:CompleteAdding和IsCompleted
  • 3. 枚举:GetConsumingEnumerable和BlockingCollection本身
  • 4. GetConsumingEnumerable和CompleteAdding

返回目录

1. 限制最大容量:BoundedCapacity

BoundedCapacity属性和CompleteAdding方法,它们都可以从某种方式上限制元素被加入到集合中。但BoundedCapacity是用来限制集合的最大容量,当容量已满后,后续的添加操作会被阻塞,一旦有元素被移除,那么阻塞的添加操作会成功执行。

比如下面代码,试图将1-50加入到BlockingCollection,此时默认内部是ConcurrentBag,当然你可以指定任意IProducerConsumerCollection。我们把BoundedCapacity设置成2。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var bcollec =newBlockingCollection<int>(2);
//试图添加1-50
Task.Run(() =>
    {
        Parallel.For(1, 51, i =>
        {
            bcollec.Add(i);
            Console.WriteLine("加入:"+ i);
        });
    });
 
Thread.Sleep(1000);
Console.WriteLine("调用一次Take");
bcollec.Take();
 
Thread.Sleep(Timeout.Infinite);

可能的输出:

加入:37

加入:13

调用一次Take

加入:25

只有最多两个可以加入,然后调用Take后,下一个元素才会被加入。(注意此时Parallel.For中会有多个线程处于阻塞状态,因为无法加入数据)。

返回目录

2. 禁止加入:CompleteAdding和IsCompleted

CompleteAdding方法则是直接不允许任何元素被加入集合,即使是当前元素的数量小于BoundedCapacity属性。

代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var bcollec =newBlockingCollection<int>(5);
//试图添加1-50
Task.Run(() =>
    {
        Parallel.For(1, 51, i =>
        {
            Console.WriteLine("准备加入:"+ i);
            bcollec.Add(i);
            Console.WriteLine("== 成功加入:"+ i);
            Thread.Sleep(1000);
        });
    });
 
//等待一小段时间后马上调用CompleteAdding
Thread.Sleep(500);
Console.WriteLine("调用CompleteAdding");
bcollec.CompleteAdding();
 
Thread.Sleep(Timeout.Infinite);

上述代码可能的输出:

准备加入:1

准备加入:13

准备加入:25

准备加入:37

== 成功加入:13

== 成功加入:1

== 成功加入:37

== 成功加入:25

调用CompleteAdding

准备加入:2

准备加入:38

准备加入:26

准备加入:14

可以看到,虽然BlockingCollection的BoundedCapacity为5,但是由于提前调用了CompleteAdding,即使当前集合只有4个元素,也不会再同意新的加入操作了。

那么CompleteAdding有什么用?当使用了CompleteAdding方法后且集合内没有元素的时候,另一个属性IsCompleted此时会为True,这个属性可以用来判断是否当前集合内的所有元素都被处理完,而BlockingCollection背后的IProducerConsumerCollection恰恰常用来处理此类生产者-消费者问题的。

下面我们首先在多个线程中试图往BlockingCollection中加入元素,然后中途调用CompleteAdding,接着通过IsCompleted属性逐个处理被成功加入的元素。

如下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var bcollec =newBlockingCollection<int>();
//试图添加1-50
Task.Run(() =>
    {
        Parallel.For(1, 51, i =>
        {
            bcollec.Add(i);
            Console.WriteLine("成功加入:"+ i);
            Thread.Sleep(1000);
        });
    });
 
//等待一小段时间后马上调用CompleteAdding
Thread.Sleep(700);
Console.WriteLine("调用CompleteAdding");
bcollec.CompleteAdding();
 
Console.WriteLine("容器元素数量:"+ bcollec.Count);
 
while (!bcollec.IsCompleted)
{
    var res = bcollec.Take();
    Console.WriteLine("取出:"+ res);
}
 
Console.WriteLine("操作完成");
 
Thread.Sleep(Timeout.Infinite);

可能的输出:

成功加入:37

成功加入:25

成功加入:13

成功加入:1

调用CompleteAdding

容器元素数量:4

取出:1

取出:37

取出:25

取出:13

操作完成

返回目录

3. 枚举:GetConsumingEnumerable和BlockingCollection本身

BlockingCollection有两种枚举方法,首先BlockingCollection本身继承自IEnumerable<T>,所以它自己就可以被foreach枚举,首先BlockingCollection包装了一个线程安全集合,那么它自己也是线程安全的,而当多个线程在同时修改或访问线程安全容器时,BlockingCollection自己作为IEnumerable会返回一个一定时间内的集合片段,也就是只会枚举在那个时间点上内部集合的元素。

看下面代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var bcollec =newBlockingCollection<int>();
//试图添加1-10
Task.Run(() =>
{
    var forOpt =newParallelOptions()
    {
        //防止在某些硬件上并发数太多
        MaxDegreeOfParallelism =2
    };
 
    Parallel.For(1, 11, forOpt, i =>
    {
        bcollec.Add(i);
        Console.WriteLine("成功加入:"+ i);
        Thread.Sleep(500);
    });
});
 
Thread.Sleep(700);
//开始枚举
Task.Run(() =>
{
    foreach (var i in bcollec)
        Console.WriteLine("输出:"+ i);
});
 
Thread.Sleep(Timeout.Infinite);

我们边加入元素边进行枚举(直接在BlockingCollection上foreach),可能的输出:

成功加入:1

成功加入:6

成功加入:2

成功加入:7

输出:1

输出:6

输出:2

输出:7

成功加入:8

成功加入:3

成功加入:4

成功加入:9

成功加入:5

成功加入:10

可以看到,BlockingCollection本身的迭代器只能反映出一时的容器内容。

而BlockingCollection还有一个GetConsumingEnumerable方法,同样返回一个IEnumerable<T>,这个可枚举的集合背后的迭代器不同于BlockingCollection本身的迭代器,它可以返回最新的加入的元素,如果当前时间段没有元素被加入,它会阻塞然后等新加进来的元素。

我们把上面的使用BlockingCollection本身枚举代码中的枚举Task改成这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//开始枚举
Task.Run(() =>
{
    foreach (var i in bcollec.GetConsumingEnumerable())
        Console.WriteLine("输出:"+ i);
    Console.WriteLine("完成枚举");
});

可能的输出:

成功加入:6

成功加入:1

成功加入:2

成功加入:7

输出:6

输出:1

输出:2

输出:7

成功加入:3

成功加入:8

输出:3

输出:8

成功加入:4

成功加入:9

输出:4

输出:9

成功加入:10

成功加入:5

输出:10

输出:5

这个迭代器很给力,一直处于等待和执行的状态,只要有新的元素被加入,它会找机会去执行foreach的内容,然后再阻塞去等新的元素。

而且在输出中,代码里的“完成枚举”字符串一直没有被输出。此时它还在卖力地等……因为它不确定什么时候才不会有新元素被加入。

返回目录

4. GetConsumingEnumerable和CompleteAdding

好,此时你应该想到了上面学的CompleteAdding方法,它可以禁止新的元素被加入到BlockingCollection的内部线程安全集合中,所以使用这个方法可以通知GetConsumingEnumerable的迭代器您老不用再等了,后面不会有元素被加进来了。

如下代码:

抱歉,这几段代码都不短,而且都类似。但我仍然把完整代码贴出来,虽然这使文章比较冗长,但是我觉得这样读者浏览或者复制时从上到下一目了然,总比看到诸如“请把前面xxx个代码做如下修改:把xxx行改成xxx,在xxx行加入这段代码……”好吧。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var bcollec =newBlockingCollection<int>();
//试图添加1-10
Task.Run(() =>
{
    var forOpt =newParallelOptions()
    {
        //防止在某些硬件上并发数太多
        MaxDegreeOfParallelism =2
    };
 
    Parallel.For(1, 11, forOpt, i =>
    {
        Console.WriteLine("等待加入:"+ i);
        bcollec.Add(i);
        Console.WriteLine("成功加入:"+ i);
        Thread.Sleep(500);
    });
});
 
Thread.Sleep(600);
//开始枚举
Task.Run(() =>
{
    foreach (var i in bcollec.GetConsumingEnumerable())
        Console.WriteLine("输出:"+ i);
    Console.WriteLine("完成枚举");
});
 
Thread.Sleep(300);
 
bcollec.CompleteAdding();
Console.WriteLine("=== 调用CompleteAdding");
 
Thread.Sleep(Timeout.Infinite);
 

可能的输出:

等待加入:1

等待加入:6

成功加入:1

成功加入:6

等待加入:2

成功加入:2

等待加入:7

成功加入:7

输出:1

输出:6

输出:2

输出:7

=== 调用CompleteAdding

完成枚举

等待加入:3

等待加入:8

可以看到,等CompleteAdding,“枚举完成”马上被输出!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CSharp编程大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
你真的知道.NET Framework中的阻塞队列BlockingCollection的妙用吗?
BlockingCollection集合是一个拥有阻塞功能的集合,它就是完成了经典生产者消费者的算法功能。一般情况下,我们可以基于 生产者 - 消费者模式来实现并发。BlockingCollection<T> 类是最好的解决方案
Ryan_OVO
2023/10/18
8550
你真的知道.NET Framework中的阻塞队列BlockingCollection的妙用吗?
线程安全集合 --- Concurrent
最近看一些代码的时候,发现有人用 System.Collections.Concurrent 下的 BlockingCollection 很便利的实现了生产者 - 消费者模式,这是之前没有注意到的,之前只关注过 ConcurrentQueue<T> 、ConcurrentStack<T> 或 ConcurrentBag<T>这些并发队列,并发堆栈,并发包相关的使用,正好好奇 BlockingCollection的用法,本次将 System.Collections.Concurrent 下的所有用法都实践一下。
郑子铭
2025/06/11
1530
线程安全集合 --- Concurrent
Thread、ThreadPool、Task、Parallel、Async和Await基本用法、区别以及弊端
ThreadPool是Thread的一个升级版,ThreadPool是从线程池中获取线程,如果线程池中又空闲的元素,则直接调用,如果没有才会创建,而Thread则是会一直创建新的线程,要知道开启一个线程就算什么事都不做也会消耗大约1m的内存,是非常浪费性能的,接下来我们写一个例子来看一下二者的区别:
AI.NET 极客圈
2019/08/14
1.9K0
【深入浅出C#】章节 9: C#高级主题:多线程编程和并发处理
多线程编程和并发处理的重要性和背景 在计算机科学领域,多线程编程和并发处理是一种关键技术,旨在充分利用现代计算机系统中的多核处理器和多任务能力。随着计算机硬件的发展,单一的中央处理单元(CPU)已经不再是主流,取而代之的是多核处理器,这使得同时执行多个任务成为可能。多线程编程允许开发人员将一个程序拆分成多个线程,这些线程可以并行执行,从而提高程序的性能和响应速度。 为什么多线程在现代应用中至关重要?
喵叔
2023/08/26
5.6K0
C# BlockingCollection
在多线程编程中,数据共享和线程同步是两个关键问题。C# 提供了一些强大的工具来帮助开发人员管理这些挑战,其中之一就是 BlockingCollection<T>。这篇博客将介绍 BlockingCollection 的基本概念、用法以及它在多线程环境中的优势。
JusterZhu
2025/01/23
1720
C# BlockingCollection
.NET 各版本多线程使用原理与实践
多线程编程是现代应用程序开发中的核心技术,尤其是在需要并发处理或提升性能的场景中。本文将以 .NET 各版本为背景,详细探讨多线程技术的发展、底层原理以及实践方法。
Michel_Rolle
2024/11/19
3.3K0
.NET Thread、Task或Parallel实现多线程的使用总结
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
郑子铭
2023/08/30
4050
.NET Thread、Task或Parallel实现多线程的使用总结
C# 看懂这100+行代码,你就真正入门了(经典)
这个方法体的作用主要是开了两个线程,第一个线程每隔1秒向集合添加一个数,第二个线程每隔一秒,便利输出集合。在这里集合要是改成list的,即:
用户9127601
2021/11/01
5610
最全C#线程查漏补缺
在单核计算机中,CPU 是独占的,内存是共享的,这时候运行一个程序的时候是没有问题。但是运行多个程序的时候,为了不发生一个程序霸占整个 CPU 不释放的情况(如一个程序死循环无法结束了,那么其他程序就没有机会运行了),就需要开发者给不同程序划分不同的执行时间。为了避免不同程序之间互相操作数据或代码,导致程序被破坏的情况,就需要开发者给程序划分独立的内存范围。也就是程序需要开发者进行调度以及和划分独立的内存空间。
郑子铭
2023/08/30
3420
最全C#线程查漏补缺
【C#】BlockingCollection 用法浅析
多线程操作集合时,ConcurrentQueue 是我常用的,一直用得也挺爽,突然发现了 BlockingCollection,原来还可以更简单。。。
丹枫无迹
2021/04/01
1.7K0
基于 CAS 无锁实现的 Disruptor.NET 居然慢于 BlockingCollection,是真的吗?
StackOverflow 有人说自己的 Disruptor.NET 代码比 BlockingCollection 还有慢 2 倍,并且把完整代码贴出,楼下几个老外也的回复说了一堆,但是没研究出个所以然来,讨论到最后甚至说可能你的场景不适合 Disruptor,我对此表示怀疑,BlockingCollection 内部实现比较简单粗暴,必要时就加锁,取数据时用信号量等待添加操作完成,而 Disruptor 是专门针对 CPU 缓存的特性优化过的,内部没有锁只有 CAS 原子操作,而且还考虑到了 false sharing,因此理论上 Disruptor 不会比 BlockingCollection 慢。
全栈程序员站长
2022/07/11
4150
项目开发中应用并发的一二事
在多线程环境下,使用BlockingCollection以及ConcurrentQueue来消费生产者生产的资源,这是我自己写的多生产者多消费者的作法,其实也是基于单个task下的阻塞队列的IsComplete来识别的。
Ryan_OVO
2023/10/18
1900
C#学习笔记 线程操作
完整代码在这里:https://github.com/techstay/csharp-learning-note 。
乐百川
2022/05/05
5100
30分钟泛型教程
一、泛型入门: 我们先来看一个最为常见的泛型类型List<T>的定义 (真正的定义比这个要复杂的多,我这里删掉了很多东西) [Serializable] public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> { public T this[int index] { get; set; } public void Add(T item); public void Clear(); public bool
liulun
2018/01/12
8120
C# 多线程九之Timer类
相信写过定时任务的小伙伴都知道这个类,非常的轻量级,而且FCL中大量的类使用了这个方法,比如CancellationTokenSource的CancelAfter就是用Timer去做的.
郑小超.
2018/12/26
9650
c#线程-线程同步
如果有多个线程同时访问共享数据的时候,就必须要用线程同步,防止共享数据被破坏。如果多个线程不会同时访问共享数据,可以不用线程同步。 线程同步也会有一些问题存在: 1、性能损耗。获取,释放锁,线程上下文建切换都是耗性能的。 2、同步会使线程排队等待执行。
苏州程序大白
2021/08/13
8590
[C#]Parallel使用
  随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能。在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Tasks。这里面有很多关于并行开发的东西,今天第一篇就介绍下最基础,最简单的——认识和使用Parallel。
云未归来
2025/07/20
960
[C#]Parallel使用
C#:异步编程和线程的使用(.NET 4.5 )
异步编程和线程处理是并发或并行编程非常重要的功能特征。为了实现异步编程,可使用线程也可以不用。将异步与线程同时讲,将有助于我们更好的理解它们的特征。 本文中涉及关键知识点 1. 异步编程 2. 线程的使用 3. 基于任务的异步模式 4. 并行编程 5. 总结 异步编程 什么是异步操作?异步操作是指某些操作能够独立运行,不依赖主流程或主其他处理流程。通常情况下,C#程序从Main方法开始,当Main方法返回时结束。所有的操作都是按顺序执行的。执行操作是有序列的,一个操作必须等到其前面的操作完成才能够
葡萄城控件
2018/01/10
1.9K0
C#:异步编程和线程的使用(.NET 4.5 )
C#多线程
主线程和通过Thread构造函数创建的线程默认都是前台线程,线程池获取的则默认是后台线程,通过 IsBackground 属性可以设置和获取当前线程是前台线程还是后台线程。
全栈程序员站长
2022/09/06
1.5K0
C#并行与多线程——Parallel并行
硬件线程又叫做逻辑内核,我们可以在”任务管理器“中查看”性能“标签页,就能查看电脑的线程数,我们常说的四核八线程,就是指这个,当然这里的四核八线程,其中的八线程是超线程技术,也就是一个核心对应两个线程,从而从硬件层面提升执行性能。
李郑
2019/12/09
5.6K0
推荐阅读
相关推荐
你真的知道.NET Framework中的阻塞队列BlockingCollection的妙用吗?
更多 >
交个朋友
加入HAI高性能应用服务器交流群
探索HAI应用新境界 共享实践心得
加入[游戏服务器] 腾讯云官方交流站
游戏服运维小技巧 常见问题齐排查
加入数据技术趋势交流群
大数据技术前瞻 数据驱动业务实践
换一批
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档