使用并发启用队列<t> </t>

时间:2010-12-29 15:22:14

标签: c# multithreading .net-3.5 queue

我之前有question我提供了解决方案;但是,因为我在.Net 3.5上,所以我无法访问ConcurrentQueue<T>。我需要Queue<T>来允许并发。我读了这个question,如果一个项目在队列中并且线程方法试图将项目出列,则似乎会出现问题。

我现在的任务是确定是否可以派生自己的并发队列类。这就是我想出的:

public sealed class ConcurrentQueue : Queue<DataTable>
{
    public event EventHandler<TableQueuedEventArgs> TableQueued;
    private ICollection que;

    new public void Enqueue(DataTable Table)
    {
        lock (que.SyncRoot)
        {
            base.Enqueue(Table);
        }

        OnTableQueued(new TableQueuedEventArgs(Dequeue()));
    }

    //  this is where I think I will have a problem...
    new public DataTable Dequeue()
    {
        DataTable table;

        lock (que.SyncRoot)
        {
            table = base.Dequeue();
        }

        return table;
    }

    public void OnTableQueued(TableQueuedEventArgs table)
    {
        EventHandler<TableQueuedEventArgs> handler = TableQueued;

        if (handler != null)
        {
            handler(this, table);
        }
    }
}

因此,当DataTable排队时,EventArgs会将出列表传递给事件订阅者。这个实现会为我提供一个线程安全的队列吗?

5 个答案:

答案 0 :(得分:3)

快速访问我最喜欢的搜索引擎,发现我的记忆是正确的; you can get the Task Parallel Library even on .NET 3.5。另请参阅The PFX team blog post on the subject以及您下载的Reactive Extensions以获取所需的System.Threading.dll

答案 1 :(得分:3)

您需要使用new来隐藏基类中的方法这一事实通常表明您应该使用组合而不是继承......

这是一个简单的同步队列,它不使用继承但仍然依赖于标准Queue<T>的行为:

public class ConcurrentQueue<T> : ICollection, IEnumerable<T>
{
    private readonly Queue<T> _queue;

    public ConcurrentQueue()
    {
        _queue = new Queue<T>();
    }

    public IEnumerator<T> GetEnumerator()
    {
        lock (SyncRoot)
        {
            foreach (var item in _queue)
            {
                yield return item;
            }
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void CopyTo(Array array, int index)
    {
        lock (SyncRoot)
        {
            ((ICollection)_queue).CopyTo(array, index);
        }
    }

    public int Count
    {
        get
        { 
            // Assumed to be atomic, so locking is unnecessary
            return _queue.Count;
        }
    }

    public object SyncRoot
    {
        get { return ((ICollection)_queue).SyncRoot; }
    }

    public bool IsSynchronized
    {
        get { return true; }
    }

    public void Enqueue(T item)
    {
        lock (SyncRoot)
        {
            _queue.Enqueue(item);
        }
    }

    public T Dequeue()
    {
        lock(SyncRoot)
        {
            return _queue.Dequeue();
        }
    }

    public T Peek()
    {
        lock (SyncRoot)
        {
            return _queue.Peek();
        }
    }

    public void Clear()
    {
        lock (SyncRoot)
        {
            _queue.Clear();
        }
    }
}

答案 2 :(得分:2)

当你将物品排入队列时,你的物品就会出列 您需要使用参数来引发事件。

它是否真的是线程安全取决于你如何使用它 如果您检查Count或检查是否空虚,那么它不是线程安全的,也不容易使线程安全。
如果不这样做,您可以使用比队列更简单的东西。

答案 3 :(得分:2)

在最初的问题之后的一段时间,我知道(这与另一个问题的权利“相关”),但在类似的情况下,我已经使用了以下内容。对CPU缓存的使用不尽如人意,但是如果操作之间经常存在较大的间隙,那么简单,无锁,线程安全以及CPU缓存的使用并不重要而不是分配的接近程度可能会减少影响:

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
}

答案 4 :(得分:0)

OnTableQueued(new TableQueuedEventArgs(Dequeue()));方法

中的Enqueue

使用Peek代替Dequeue

应该是

OnTableQueued(new TableQueuedEventArgs(base.Peek()));