我们通过C# Queue 和Stack的实现知道Stack是依靠数组实现的,那么ConcurrentStack的栈又是如何实现的了,然后它的线程安全又是怎么做到的了? 来看看其code吧

 public class ConcurrentStack<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>
{
private class Node
{
internal readonly T m_value; // Value of the node.
internal Node m_next; // Next pointer.
internal Node(T value)
{
m_value = value;
m_next = null;
}
}
private volatile Node m_head;
private const int BACKOFF_MAX_YIELDS = ; public ConcurrentStack(){}
public ConcurrentStack(IEnumerable<T> collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
InitializeFromCollection(collection);
} private void InitializeFromCollection(IEnumerable<T> collection)
{
// We just copy the contents of the collection to our stack.
Node lastNode = null;
foreach (T element in collection)
{
Node newNode = new Node(element);
newNode.m_next = lastNode;
lastNode = newNode;
} m_head = lastNode;
} public void Push(T item)
{
Node newNode = new Node(item);
newNode.m_next = m_head;
if (Interlocked.CompareExchange(ref m_head, newNode, newNode.m_next) == newNode.m_next)
{
return;
}
PushCore(newNode, newNode);
} private void PushCore(Node head, Node tail)
{
SpinWait spin = new SpinWait();
do
{
spin.SpinOnce();
// Reread the head and link our new node.
tail.m_next = m_head;
}
while (Interlocked.CompareExchange(ref m_head, head, tail.m_next) != tail.m_next); } public bool TryPop(out T result)
{
Node head = m_head;
//stack is empty
if (head == null)
{
result = default(T);
return false;
}
if (Interlocked.CompareExchange(ref m_head, head.m_next, head) == head)
{
result = head.m_value;
return true;
}
return TryPopCore(out result);
} private bool TryPopCore(out T result)
{
Node poppedNode; if (TryPopCore(, out poppedNode) == )
{
result = poppedNode.m_value;
return true;
} result = default(T);
return false; }
private int TryPopCore(int count, out Node poppedHead)
{
SpinWait spin = new SpinWait();
Node head;
Node next;
int backoff = ;
Random r = new Random(Environment.TickCount & Int32.MaxValue); // avoid the case where TickCount could return Int32.MinValue
while (true)
{
head = m_head;
// Is the stack empty?
if (head == null)
{
poppedHead = null;
return ;
}
next = head;
int nodesCount = ;
for (; nodesCount < count && next.m_next != null; nodesCount++)
{
next = next.m_next;
} // Try to swap the new head. If we succeed, break out of the loop.
if (Interlocked.CompareExchange(ref m_head, next.m_next, head) == head)
{
poppedHead = head;
return nodesCount;
} // We failed to CAS the new head. Spin briefly and retry.
for (int i = ; i < backoff; i++)
{
spin.SpinOnce();
} backoff = spin.NextSpinWillYield ? r.Next(, BACKOFF_MAX_YIELDS) : backoff * ;
}
}
}

ConcurrentStack<T>里面有一个内部类Node,看到这里我们就知道ConcurrentStack<T>的栈是一开节点Node来做的一个链表,非常好理解。那么线程安全又是怎么做到的了?首先我们来看看Push放法,首先我们需要新实例一个Node,并且新Node的m_next指向现有m_head头节点【newNode.m_next = m_head】,然后在原子比较newNode.m_next 是否是m_head【Interlocked.CompareExchange(ref m_head, newNode, newNode.m_next) == newNode.m_next】,如果是那么把m_head改为newNode ,push操作完成。如果第一个线程newNode.m_next = m_head之后,有新的线程执行了Interlocked.CompareExchange(ref m_head, newNode, newNode.m_next) == newNode.m_next 那么push就需要执行PushCore方法;该方法先自旋一下,然后在Interlocked.CompareExchange(ref m_head, head, tail.m_next) != tail.m_next【这里head和tail是新节点,tail.m_next是指向m_head】,如果当前线程是最新最近的那个 ,那么这个Interlocked.CompareExchange(ref m_head, head, tail.m_next) == tail.m_next就为true,退出循环,否者自旋后再次比较赋值。那么TryPop的实现也是类似的,如果if (Interlocked.CompareExchange(ref m_head, head.m_next, head) == head)成立那么直接返回,否者调用TryPopCore方法。而TryPopCore方法的核心是   if (Interlocked.CompareExchange(ref m_head, next.m_next, head) == head),如果成立则退出,否者自旋,至于自旋的次数来源于for (int i = 0; i < backoff; i++){ spin.SpinOnce(); }。是不是很简单了,但是也很巧妙啊。线程安全依靠SpinWait 的自旋和原子操作Interlocked.CompareExchange来实现的。

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

  1. .Net中的并行编程-2.ConcurrentStack的实现与分析

    在上篇文章<.net中的并行编程-1.基础知识>中列出了在.net进行多核或并行编程中需要的基础知识,今天就来分析在基础知识树中一个比较简单常用的并发数据结构--.net类库中无锁栈的实现 ...

  2. C#的队列(Queue,ConcurrentQueue)和堆栈(Stack,ConcurrentStack)

    一.Queue 表示对象的先进先出(FIFO)集合,非线程安全 常用方法   Dequeue 入队 Enqueue 出队 Contains 队列中是否存在某元素 Clear 清空队列 封装: /// ...

  3. ConcurrentDictionary,ConcurrentStack,ConcurrentQueue

    static void Main(string[] args) { var concurrentDictionary = new ConcurrentDictionary<int, string ...

  4. .Net多线程编程—并发集合

    并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...

  5. 论C#逼格手册

    水文.如何让自己的代码看起来,更有逼格? 要想让自己的代码,看起来更优雅,更有逼格,更高大上,就一定要写出晦涩难懂,而又简洁的代码来. 对于类自身的全局变量,一定要加this,对于基类的,一定要加ba ...

  6. 写自己的Socket框架(一)

    本系列仅介绍可用于生产环境的C#异步Socket框架,如果您在其他地方看到类似的代码,不要惊讶,那可能就是我在参考开源代码时,直接“剽窃”过来的. 1.在脑海里思考一下整个socket的链接的处理流程 ...

  7. C# - 集合类

    C#的集合类命名空间介绍: // 程序集 mscorlib.dll System.dll System.Core.dll // 命名空间 using System.Collections:集合的接口和 ...

  8. (转)C# 选择正确的集合

    原文: http://www.cnblogs.com/luminji/archive/2011/03/24/1993393.html 要选择正确的集合,我们首先要了解一些数据结构的知识.所谓数据结构, ...

  9. C#并发编程

    并发编程,一直是小白变成(●—●)的一个坎.平时也用到过不少并发编程操作,在这里进行一下记录. 多线程并不是唯一 并发:同时做多件事情. 多线程:并发的一种形式,采用多线程来执行程序. 并行处理:把正 ...

随机推荐

  1. Maven实战(Maven+Nexus建立私服【Linux系统】)

    准备工作 下载及配置Maven3:http://www.cnblogs.com/leefreeman/archive/2013/03/05/2944519.html 下载Nexus:http://ne ...

  2. TP5.1:request请求对象(使用四种方式获取)

    准备: 在index/controller下创建一个名为requests.php的文件(注意:不要起名为request,因为它是关键字,不被允许起名) 动态方法和静态方法的区别: 静态方法:publi ...

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

  4. linux 下 eclipse 安装

    下载: 官网选择相应安装包下载,我这里下了tar.gz包 安装: tar xzvf eclipse-inst-linux64.tar.gz 设置环境变量 export JAVA_HOME=/usr/l ...

  5. poj1742 多维背包

    普通的多维背包做不了,需要优化一下 但是没有学优化..别的方法也是可以做的 省去一个 表示阶段的 i 维度,dp[j]表示面值为j的钱是否被凑出来了,used[j]表示第i种硬币在凑面值为j的时候被用 ...

  6. hdu3193 降维+rmq

    /* 给定n个数对(p,d),如果有这么一个数对(pi,di),其他所有的数对都不能严格小于这个数对 请求出有多少个这样的数对! 解法:对于数对(p,d),能找到在[1,p-1]之间的小于d的数对,那 ...

  7. Python 多环境配置管理

    一.概述 实际工程开发中常常会对开发.测试和生产等不同环境配置不同的数据库环境,传统方式可以通过添加不同环境的配置文件达到部署时的动态切换的效果.这种方式还不错,不过不同环境间往往会共享相同的配置而造 ...

  8. 《剑指offer》-前n项和不准用通解和各种判断

    题目描述 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 这题目简直没事找事...为啥这么说,因为没有限 ...

  9. 合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)

    尽量使用对象组合,而不是继承来达到复用的目的 未完待续

  10. vtiger7菜单管理

    添加了新模块,但是菜单上却没显示. 和菜单相关的表有4张 我们要把新建的message放到support模块下面 1.把默认的父级目录tools改成support 2. app2tab 0表示不显示, ...