C# ConcurrentStack实现
我们通过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实现的更多相关文章
- .Net中的并行编程-2.ConcurrentStack的实现与分析
在上篇文章<.net中的并行编程-1.基础知识>中列出了在.net进行多核或并行编程中需要的基础知识,今天就来分析在基础知识树中一个比较简单常用的并发数据结构--.net类库中无锁栈的实现 ...
- C#的队列(Queue,ConcurrentQueue)和堆栈(Stack,ConcurrentStack)
一.Queue 表示对象的先进先出(FIFO)集合,非线程安全 常用方法 Dequeue 入队 Enqueue 出队 Contains 队列中是否存在某元素 Clear 清空队列 封装: /// ...
- ConcurrentDictionary,ConcurrentStack,ConcurrentQueue
static void Main(string[] args) { var concurrentDictionary = new ConcurrentDictionary<int, string ...
- .Net多线程编程—并发集合
并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...
- 论C#逼格手册
水文.如何让自己的代码看起来,更有逼格? 要想让自己的代码,看起来更优雅,更有逼格,更高大上,就一定要写出晦涩难懂,而又简洁的代码来. 对于类自身的全局变量,一定要加this,对于基类的,一定要加ba ...
- 写自己的Socket框架(一)
本系列仅介绍可用于生产环境的C#异步Socket框架,如果您在其他地方看到类似的代码,不要惊讶,那可能就是我在参考开源代码时,直接“剽窃”过来的. 1.在脑海里思考一下整个socket的链接的处理流程 ...
- C# - 集合类
C#的集合类命名空间介绍: // 程序集 mscorlib.dll System.dll System.Core.dll // 命名空间 using System.Collections:集合的接口和 ...
- (转)C# 选择正确的集合
原文: http://www.cnblogs.com/luminji/archive/2011/03/24/1993393.html 要选择正确的集合,我们首先要了解一些数据结构的知识.所谓数据结构, ...
- C#并发编程
并发编程,一直是小白变成(●—●)的一个坎.平时也用到过不少并发编程操作,在这里进行一下记录. 多线程并不是唯一 并发:同时做多件事情. 多线程:并发的一种形式,采用多线程来执行程序. 并行处理:把正 ...
随机推荐
- js----jquery和js的区别
1.在htlm页面中引入jquery文件后,想获取<input>输入框的数据 <input type='text' id = 'username>' var text = $( ...
- ZOJ 3229 Shoot the Bullet(有源汇上下界最大流)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3442 题目大意: 一个屌丝给m个女神拍照,计划拍照n天,每一天屌丝给 ...
- yield()方法就是礼让,具体还是看cpu怎么分配
package charpter07; //yield():礼让的行为public class Processor implements Runnable { @Override public voi ...
- 2017-2018-2 20155309南皓芯《网络对抗技术》Exp2 后门原理与实践
实验要求 (1)使用netcat获取主机操作Shell,cron启动 (0.5分) (2)使用socat获取主机操作Shell, 任务计划启动 (0.5分) (3)使用MSF meterpreter( ...
- bootstrap改变上传文件按钮样式,并显示已上传文件名
参考博文: html中,文件上传时使用的<input type="file">的样式自定义 html中<input type="file"&g ...
- maven的三种工程pom、jar、war
阅读数:739 maven中的三种工程: 1.pom工程:用在父级工程或聚合工程中.用来做jar包的版本控制. 2.war工程:将会打包成war,发布在服务器上的工程.如网站或服务. 3.jar工程: ...
- js中setInterval和setTimeout区别和用法
setTimeout setTimeout() //- 在指定时间后执行代码clearTimeout() //- 取消 setTimeout(),clearTimeout()方法的参数必须是由setT ...
- 域名解析到Nginx服务器项目上
第一步:先将域名解析到 IP 上 解析完后,如下 第二步:Nginx服务器配置servername 修改 /usr/local/nginx/conf/nginx.conf (你的配置文件可能不在这个目 ...
- docker容器中安装vim 、telnet、ifconfig, ping命令
在使用docker容器时,有时候里边没有安装vim,敲vim命令时提示说:vim: command not found,这个时候就需要安装vim,可是当你敲apt-get install vim命令时 ...
- poj 2253 Frogger (最小最大路段)【dijkstra】
<题目链接> 题目大意: 给出青蛙A,B和若干石头的坐标,现青蛙A想到青蛙B那,A可通过任意石头到达B,问从A到B多条路径中最小的最长边. 解题分析: 这是最短路的一类典型题目,与普通的最 ...