我们从C# Queue 和Stack的实现知道Queue是用数组来实现的,数组的元素不断的通过Array.Copy从一个数组移动到另一个数组,ConcurrentQueue我们需要关心2点:1线程安全是怎么实现的,2队列又是怎么实现的?我们来看看其实现code:

public interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection
{
void CopyTo(T[] array, int index);
bool TryAdd(T item);
bool TryTake(out T item);
T[] ToArray();
} public class ConcurrentQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
{
[NonSerialized]
private volatile Segment m_head; [NonSerialized]
private volatile Segment m_tail; private T[] m_serializationArray; // Used for custom serialization. private const int SEGMENT_SIZE = ; //number of snapshot takers, GetEnumerator(), ToList() and ToArray() operations take snapshot.
[NonSerialized]
internal volatile int m_numSnapshotTakers = ; public ConcurrentQueue()
{
m_head = m_tail = new Segment(, this);
} public ConcurrentQueue(IEnumerable<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
} InitializeFromCollection(collection);
} private void InitializeFromCollection(IEnumerable<T> collection)
{
Segment localTail = new Segment(, this);//use this local variable to avoid the extra volatile read/write. this is safe because it is only called from ctor
m_head = localTail; int index = ;
foreach (T element in collection)
{
Contract.Assert(index >= && index < SEGMENT_SIZE);
localTail.UnsafeAdd(element);
index++; if (index >= SEGMENT_SIZE)
{
localTail = localTail.UnsafeGrow();
index = ;
}
} m_tail = localTail;
} public void Enqueue(T item)
{
SpinWait spin = new SpinWait();
while (true)
{
Segment tail = m_tail;
if (tail.TryAppend(item))
return;
spin.SpinOnce();
}
} public bool TryDequeue(out T result)
{
while (!IsEmpty)
{
Segment head = m_head;
if (head.TryRemove(out result))
return true;
//since method IsEmpty spins, we don't need to spin in the while loop
}
result = default(T);
return false;
} private class Segment
{
internal volatile T[] m_array;
internal volatile VolatileBool[] m_state;
private volatile Segment m_next;
internal readonly long m_index;
private volatile int m_low;
private volatile int m_high;
private volatile ConcurrentQueue<T> m_source; internal Segment(long index, ConcurrentQueue<T> source)
{
m_array = new T[SEGMENT_SIZE];
m_state = new VolatileBool[SEGMENT_SIZE]; //all initialized to false
m_high = -;
Contract.Assert(index >= );
m_index = index;
m_source = source;
} internal void UnsafeAdd(T value)
{
Contract.Assert(m_high < SEGMENT_SIZE - );
m_high++;
m_array[m_high] = value;
m_state[m_high].m_value = true;
} internal Segment UnsafeGrow()
{
Contract.Assert(m_high >= SEGMENT_SIZE - );
Segment newSegment = new Segment(m_index + , m_source); //m_index is Int64, we don't need to worry about overflow
m_next = newSegment;
return newSegment;
} internal void Grow()
{
//no CAS is needed, since there is no contention (other threads are blocked, busy waiting)
Segment newSegment = new Segment(m_index + , m_source); //m_index is Int64, we don't need to worry about overflow
m_next = newSegment;
Contract.Assert(m_source.m_tail == this);
m_source.m_tail = m_next;
} internal bool TryAppend(T value)
{
if (m_high >= SEGMENT_SIZE - )
{
return false;
}
try
{ }
finally
{
newhigh = Interlocked.Increment(ref m_high);
if (newhigh <= SEGMENT_SIZE - )
{
m_array[newhigh] = value;
m_state[newhigh].m_value = true;
} if (newhigh == SEGMENT_SIZE - )
{
Grow();
}
} return newhigh <= SEGMENT_SIZE - ;
} internal bool TryRemove(out T result)
{
SpinWait spin = new SpinWait();
int lowLocal = Low, highLocal = High;
while (lowLocal <= highLocal)
{
if (Interlocked.CompareExchange(ref m_low, lowLocal + , lowLocal) == lowLocal)
{ SpinWait spinLocal = new SpinWait();
while (!m_state[lowLocal].m_value)
{
spinLocal.SpinOnce();
}
result = m_array[lowLocal]; if (m_source.m_numSnapshotTakers <= )
{
m_array[lowLocal] = default(T); //release the reference to the object.
} if (lowLocal + >= SEGMENT_SIZE)
{ spinLocal = new SpinWait();
while (m_next == null)
{
spinLocal.SpinOnce();
}
Contract.Assert(m_source.m_head == this);
m_source.m_head = m_next;
}
return true;
}
else
{ spin.SpinOnce();
lowLocal = Low; highLocal = High;
}
}//end of while
result = default(T);
return false;
}
}
}

首先ConcurrentQueue构造函数没有 int capacity参数了,里面的线程安全是用SpinWait自旋来实现的,当我想往队列ConcurrentQueue添加一个元素的时候,如果添加失败,那程序自旋等待一下,再次添加元素,直到添加成功。里面用到了一个Segment自定义的类型,Segment的m_array是一个含有32个元素的数组,m_state和m_array一一对应,主要是用来标记m_array里面的元素是否有效。m_next是用来连接到下一个Segment的,m_high与添加元素密切相关,m_low与移除元素有关。先看TryAppend方法,优先将当前的newhigh原子加1【newhigh = Interlocked.Increment(ref m_high);】,这样假如有多个线程同时添加元素,每一个线程拿到的newhigh 值不用,那么它们操作m_array的下标就不同了,所以彼此之间不影响,到现在添加的线程安全就明白了。那么队列又是如何实现的了?我们来看看Grow()方法,当Segment的32个元素都被使用了,那么这个时候添加元素需要扩容,扩容的方式是重新实例一个Segment,旧的Segment的m_next属性指向新的得Segment,这样就组成了一个Segment链表(Segment核心是数组),它们的index从0开始,第一个Segment的index为0,第2个Segment的index为1....。TryRemove的实现类似,m_low其实是Segment的m_array下标,程序自旋一次,查找是否有元素,如果有元素必须检查元素是否有效(!m_state[lowLocal].m_value,因为在天加元素的时候是先增加newhigh变量,然后在设置m_state[newhigh].m_value = true有效),所以移除元素的时候必选验证元素是否有效。然后释放元素m_array[lowLocal] = default(T);,如果第一个Segment的元素全部移除了【if (lowLocal + 1 >= SEGMENT_SIZE)】,那么我们就应该开始移除第2个Segment元素了,需要检查是否有第2个Segment,如果没有 就自旋等待吧,然后m_head指向第下一个Segment【m_source.m_head = m_next;】线程安全依靠SpinWait 的自旋和原子操作Interlocked.Increment和Interlocked.CompareExchange来实现的。

C# ConcurrentQueue实现的更多相关文章

  1. .Net中的并行编程-3.ConcurrentQueue实现与分析

    在上文<.Net中的并行编程-2.ConcurrentQueue的实现与分析> 中解释了无锁的相关概念,无独有偶BCL提供的ConcurrentQueue也是基于原子操作实现, 由于Con ...

  2. C# 同步/并发队列ConcurrentQueue

    如下所示,ConcurrentQueue做到了代码的简化,在并发模型中充当同步对象 private ConcurrentQueue<string> inQueue = new Concur ...

  3. c#高效的线程安全队列ConcurrentQueue<T>(上)

      ConcurrentQueue<T>队列是一个高效的线程安全的队列,是.Net Framework 4.0,System.Collections.Concurrent命名空间下的一个数 ...

  4. Performance Test of List<T>, LinkedList<T>, Queue<T>, ConcurrentQueue<T>

    //Test Group 1 { var watch = Stopwatch.StartNew(); var list = new List<int>(); ; j < ; j++) ...

  5. C# 同步/并发队列ConcurrentQueue (表示线程安全的先进先出 (FIFO) 集合)

    http://msdn.microsoft.com/zh-cn/library/dd267265(v=vs.110).aspx static void Main(string[] args) { // ...

  6. ConcurrentQueue对列的基本使用方式

    队列(Queue)代表了一个先进先出的对象集合.当您需要对各项进行先进先出的访问时,则使用队列.当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队. ConcurrentQueue< ...

  7. C#-----线程安全的ConcurrentQueue<T>队列

     ConcurrentQueue<T>队列是一个高效的线程安全的队列,是.Net Framework 4.0,System.Collections.Concurrent命名空间下的一个数据 ...

  8. 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...

  9. 线程安全的ConcurrentQueue<T>队列

    队列(Queue)代表了一个先进先出的对象集合.当您需要对各项进行先进先出的访问时,则使用队列.当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队. ConcurrentQueue< ...

随机推荐

  1. webpack 3之hash、chunkhash和contenthash三者的区别

    在使用webpack 3中,文件名的hash值可以有三种hash生成方式,那具体使用哪一种呢? 1.hash 如果都使用hash的话,所有文件的hash都是一样的,而且每次修改任何一个文件,所有文件名 ...

  2. C++ code:数值计算之辛普生(Simpson)法求解积分问题

  3. 深度学习Bible学习笔记:第六章 深度前馈网络

    第四章 数值计算(numerical calculation)和第五章 机器学习基础下去自己看. 一.深度前馈网络(Deep Feedfarward Network,DFN)概要: DFN:深度前馈网 ...

  4. day6作业--游戏人生完善

    本节作业: 熟练使用类和模块,写一个交互性强.有冲突的程序.     一.作业目的 1.规范程序写法,要按照模块来规范书写: 2.类的使用,文件之间的调用练习: 3.思路的开阔,自己编写冲突,实现调用 ...

  5. python全栈开发day18-模块和导入

    1.昨日内容回顾 2.模块和模块导入 1.什么是模块,为什么要模块? py文件就是模块, 把相似的功能放到一个文件,要用的时候 引入就可以直接调用了. import py文件名,导入模块就是执行他的代 ...

  6. java.lang.NumberFormatException: multiple points问题

    一般这种问题主要是因为SimpleDateFormat在多线程环境下,是线程不安全的,所以如果你在多线程环境中共享了SimpleDateFormat的实例,比如你在类似日期类中定义了一个全局的Simp ...

  7. 手动部署 kubernetes 1.9 记录

    前言 目前 kubernetes 正式版本已经到1.10版本.因为前面有大佬(漠然)已经采完坑,所以自己也试着部署 kubernetes 1.9 体验下该版本的新特性.对于前面部署的 kubernet ...

  8. 取消a标签或者onclick在移动端点击时的背景颜色

    一.取消a标签在移动端点击时的蓝色 -webkit-tap-highlight-color: rgba(, , , ); -webkit-user-select: none; -moz-user-fo ...

  9. 017 在SecureCRT中安装rz小工具

    1.安装yum 2.上传本地的文件进虚拟机 3.注意点 只是属于SecureCRT的命令,同时,在上传的位置是现在所在的位置 4.测试

  10. hdu 1622 Trees on the level(二叉树的层次遍历)

    题目链接:https://vjudge.net/contest/209862#problem/B 题目大意: Trees on the level Time Limit: 2000/1000 MS ( ...