• 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来实现的

  • 相关阅读:
    输入流输出流
    WIN32_FIND_DATA
    typeid
    为什么要内存对齐 Data alignment: Straighten up and fly right
    fatal error C1010: 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include "stdafx.h
    memcmp
    DPI
    英寸、 Picas、 点、 跨度和 Twips 之间的关系
    如何解决 “fatal error C1083: ”无法打开包括文件
    hdoj 1269迷宫城堡解题报告
  • 原文地址:https://www.cnblogs.com/majiang/p/7880380.html
Copyright © 2020-2023  润新知