如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列
创始人
2024-11-20 20:02:44
0次

一、ConcurrentQueue内部结构:

如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列

1.实现原理

众所周知,在普通的非线程安全队列有两种实现方式:

1.使用数组实现的循环队列。

2.使用链表实现的队列。

先看看两种方式的优劣:

     .Net Farmework中的普通队列Queue的实现使用了第一种方式,缺点是当队列空间不足会进行扩容,扩容的主要实现是开辟一个原始长度2倍的新数组,然后将原始数组里面的数据复制到新数组中,所以当扩容时就会产生不小的内存开销,在并发的环境中对性能的影响不可小视。当然在调用Queue的构造函数时可以指定默认空间的大小,但是一般情况下数据量是不可预测的,选大了会照成空间浪费,选小了会有复制内存的开销,而且队列扩容以后需要显示调用TrimToSize()方法才能回收掉不使用的内存空间。

     第二种链表实现方式虽然消除了空间浪费的问题但是又增加了GC的压力,当入队时会分配一个新节点,出队时要对该节点进行废弃,对于大量的出队入队操作时该实现方式性能不高。

    综合以上两种实现方式,在支持多线程并发出队并发入队的情况下,ConcurrentQueue使用了分段存储的概念(如上图所示),ConcurrentQueue分配内存时以段(Segment)为单位,一个段内部含有一个默认长度为32的数组和执行下一个段的指针,有个和Head和Tail指针分别指向了起始段和结束段(这种结构有点像操作系统的段式内存管理和页式内存管理策略)。这种分配内存的实现方式不但减轻的GC的压力而且调用者也不用显示的调用TrimToSize()方法回收内存(在某段内存为空时,会由GC来回收该段内存)。

2.Segment(段)内部结构

其实对于ConcurrentQueue的操作其实就是对Segment(数据段)的操作。

Segment可抽象出如下数据结构:

如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列

Segment内部主要方法:

如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列

    Segment内部和用数组实现的普通队列相当,只不过对于入队和出队操作使用了原子操作来防止多线程竞争问题,使用随机退让等技术保证活锁等问题,实现机制和ConcurrentStack差别不大,跟多TryAppend的实现细节在源码注释中已经阐述的非常清楚这里就再做不过多的解释。

二、入队操作

如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列

如上图所示,入队操作是在尾部的段中进行,当数据进入段内失败时会先进行一个回退操作然后再不断尝试直到成功,这里失败的原因(tail.Append(item)返回false)只有一个就是当该段内的空间不够时正在分配新的段,这段时间内会进入该段的元素会失败。

三、出队操作

如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列

如上图所示,出队失败时返回false 而不是像入队一样进行回退操作,因为出队失败的原因只有一个就是当队列内所有段的元素为空时,所以出队设计成了返回bool值的函数。

四、判断是否为空(IsEmpty)

  整个判断为O(1)的复杂度 主要有三种情况:

1. 头节点(段)不为空返回false

2. 头节点为空而且下一个节点也为空返回true

3. 头节点为空而且下一个节点不为空返回false,这种情况说明队列正在扩容,所以要自选等待扩容完毕时再次进行判断

五、获取队列内元素数量(Count)

如何在c#中利用ConcurrentQueue<T>实现一个线程安全队列

     找到头节点的low的位置和尾节点的high的位置,由于每个段内记录了当前段在队列中的索引,所以很容易求出整个队列中元素的数量。

     跟ConcurrentStack一样 微软官方文档和注释中也说明:判断队列是否为空要使用IsEmpty属性而不是判断Count == 0  原因在于GetHeadTailPositions在大量数据入队和出队的过程中寻找头尾节点的位置是比较耗时的操作,要不断循环确定头尾节点的位置,所以判断队列是否为空还是使用IsEmpty属性。

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...