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 = 8; 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(1, out poppedNode) == 1) { 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 = 1; 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 0; } next = head; int nodesCount = 1; 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 = 0; i < backoff; i++) { spin.SpinOnce(); } backoff = spin.NextSpinWillYield ? r.Next(1, BACKOFF_MAX_YIELDS) : backoff * 2; } } }
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来实现的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2016-11-22 VS SuppressMessage忽略特定方法的警告信息
2012-11-22 Asp.Net MVC 项目预编译 View