代码之家  ›  专栏  ›  技术社区  ›  blizpasta

使用静态队列线程是否安全?

  •  9
  • blizpasta  · 技术社区  · 15 年前

    msdn文档声明静态通用队列是线程安全的。这是否意味着以下代码是线程安全的?换句话说,当一个线程将一个int排队,另一个线程同时将一个int排队时,是否存在问题?为了线程安全,是否必须锁定入队列和出队列操作?

    class Test {
        public static Queue<int> queue = new Queue<int>(10000);
    
        Thread putIntThread;
        Thread takeIntThread;
    
        public Test() {
            for(int i = 0; i < 5000; ++i) {
                queue.Enqueue(0);
            }
            putIntThread = new Thread(this.PutInt);
            takeIntThread = new Thread(this.TakeInt);
            putIntThread.Start();
            takeIntThread.Start();
        }
    
        void PutInt() {
            while(true)
            {
                if(queue.Count < 10000) {//no need to lock here as only itself can change this condition
                    queue.Enqueue(0);
                }
            }
        }
    
        void TakeInt() {
            while(true) {
                if(queue.Count > 0) {//no need to lock here as only itself can change this condition
                    queue.Dequeue();
                }
            }
        }
    
    }
    

    编辑:我必须使用.NET 3.5

    4 回复  |  直到 15 年前
        1
  •  23
  •   Jon Skeet    15 年前

    Queue<T>

    Encoding.GetEncoding Enqueue Dequeue

    ConcurrentQueue<T>

        2
  •  4
  •   Jon Hanna    15 年前

    ICollection<T> Queue<T>

    internal sealed class LockFreeQueue<T>
    {
      private sealed class Node
      {
        public readonly T Item;
        public Node Next;
        public Node(T item)
        {
          Item = item;
        }
      }
      private volatile Node _head;
      private volatile Node _tail;
      public LockFreeQueue()
      {
        _head = _tail = new Node(default(T));
      }
    #pragma warning disable 420 // volatile semantics not lost as only by-ref calls are interlocked
      public void Enqueue(T item)
      {
        Node newNode = new Node(item);
        for(;;)
        {
          Node curTail = _tail;
          if (Interlocked.CompareExchange(ref curTail.Next, newNode, null) == null)   //append to the tail if it is indeed the tail.
          {
            Interlocked.CompareExchange(ref _tail, newNode, curTail);   //CAS in case we were assisted by an obstructed thread.
            return;
          }
          else
          {
            Interlocked.CompareExchange(ref _tail, curTail.Next, curTail);  //assist obstructing thread.
          }
        }
      }    
      public bool TryDequeue(out T item)
      {
        for(;;)
        {
          Node curHead = _head;
          Node curTail = _tail;
          Node curHeadNext = curHead.Next;
          if (curHead == curTail)
          {
            if (curHeadNext == null)
            {
              item = default(T);
              return false;
            }
            else
              Interlocked.CompareExchange(ref _tail, curHeadNext, curTail);   // assist obstructing thread
          }
          else
          {
            item = curHeadNext.Item;
            if (Interlocked.CompareExchange(ref _head, curHeadNext, curHead) == curHead)
            {
              return true;
            }
          }
        }
      }
    #pragma warning restore 420
    }
    

    Enqueue TryDequeue

    internal sealed class NotLockFreeYetQueue<T>
    {
      private sealed class Node
      {
        public readonly T Item;
        public Node Next{get;set;}
        public Node(T item)
        {
          Item = item;
        }
      }
      private Node _head;
      private Node _tail;
      public NotLockFreeYetQueue()
      {
        _head = _tail = new Node(default(T));
      }
      public void Enqueue(T item)
      {
        Node newNode = new Node(item);
        _tail.Next = newNode;
        _tail = newNode;
      }
      public bool TryDequeue(out T item)
      {
          if (_head == _tail)
          {
              item = default(T);
              return false;
          }
          else
          {
            item = _head.Next.Item;
            _head = _head.Next;
            return true;
          }
      }
    }
    

    Item Next readonly

    _head _tail null

    Interlocked.CompareExchange()

    private sealed class Node
    {
      public readonly T Item;
      public Node Next;
      public Node(T item)
      {
        Item = item;
      }
    }
    /* ... */
    private volatile Node _tail;
    /* ... */
    public void Enqueue(T item)
    {
      Node newNode = new Node(item);
      for(;;)
      {
        Node curTail = _tail;
        if(Interlocked.CompareExchange(ref curTail.Next, newNode, null) == null)
        {
          _tail = newNode;
          return;
        }
      }
    }
    

    volatile CompareExchange

      for(;;)
      {
        Node curTail = _tail;
        if(Interlocked.CompareExchange(ref curTail.Next, newNode, null) == null)
        {
          Interlocked.CompareExchange(ref _tail, newNode, curTail);   //CAS in case we were assisted by an obstructed thread.
    
          return;
        }
        else
        {
          Interlocked.CompareExchange(ref _tail, curTail.Next, curTail);  //assist obstructing thread.
        }
      }
    

    curTail.Next curtail.Next

    _head.Next

    private volatile Node _head;
    

      public bool TryDequeue(out T item)
      {
          Node curHead = _head;
          Node curTail = _tail;
          Node curHeadNext = curHead.Next;
          if (_head == _tail)
          {
              item = default(T);
              return false;
          }
          else
          {
            item = curHeadNext.Item;
            if (Interlocked.CompareExchange(ref _head, curHeadNext, curHead) == curHead)
              return true;
          }
      }
    

    curHeadNext.Item

    _head == _tail _tail.Next curHeadNext

    if (curHead == curTail)
    {
        if (curHeadNext == null)
        {
            item = default(T);
            return false;
        }
        else
            Interlocked.CompareExchange(ref _tail, curHeadNext, curTail);
    }
    

    byref

        3
  •  2
  •   Timores    15 年前

    msdn声明队列的静态方法是线程安全的,而不是静态实例的实例方法是线程安全的。

        4
  •  1
  •   Jonas Elfström    15 年前

    是的,你必须锁上 MSDN

    要允许多个线程访问集合进行读写,必须实现自己的同步。