因为ConcurrentQueue<T>是线程安全的是Queue<T>的替代品,由于是线程安全的,所以它不会暴露或提供Clear方法给你,所以需要使用TryDequeue来尝试安全出队,以至于不影响其它线程的操作。
static void Main(string[] args){。
ConcurrentQueue<String> cqueue = new ConcurrentQueue<string>();。
cqueue.Enqueue("a");。
cqueue.Enqueue("b");。
String result = "";。
while (cqueue.TryDequeue(out result))。
{
Console.WriteLine(result);。
}
Console.WriteLine("Queue Size="+cqueue.Count);。
Console.ReadKey(true);。
}
ConcurrentQueue是线程安全的Queue,它能保证每次只有一个线程能访问它,但需不需要缓冲要看你的代码如何实现。
http://blog.csdn.net/okletsgo007/article/details/28898463。
首先说一下,Queue队列本身就是实现先进先出的一个数据结构,从队列尾部添加元素,而从队列的头部取出元素,没有任何规则可以从头部加入一个元素的,Queue只提供从队尾加入元素的成员方法Enqueue();所以不能从头部添加元素的。
Dequeue()成员方法会在访问头部元素并将其从队列中移除;如果你是需要首个元素“移出后立即放回去”差不多的意思就是访问首个元素而不移除,那么Peek()成员方法就是访问首个头部元素,并不从队列中移除——这或许是你的需要。
事实上,不移队与移除后放回去的需求并不一样,如果你是先进行移除,并检查头部元素是否为指定的条件,达到指定条件再放回去——不妨使用Peek()先检查元素,达不到条件则移出也是一样的,不过我想不到这种作法到底是什么样的目的——如果不移除第一个元素,你可能永远操作不到第二个元素!如果你真有这方面的目的时不如使用线性表。
再者我说过你的移除后再塞回去的目的,同时涉及到另一个复杂的问题——多线程,在多线程操作时,这种行为与检查Peek()是本身有区别的(其实把你的移除——放回做为两个操作,并不是一个原子的操作)。所以这里边的涉及的问题会导致其他情况的发生。
就算如此,Dequeue/Enqueue及Peek()等相关的操作也不是什么原子操作,所以如果真是在多线程使用时不妨考虑另一个类ConcurrentQueue,这个类其实没有啥其他的作用,就是Queue的线程安全版本而已。
说白了,Queue不是线程安全版本,但性能较高,而ConcurrentQueue是线程安全版本,如果是多线程操作时,可以直接使用这个,而避免使用Queue,然后在进行多线程时的封装操作。
所以我不知道你为什么在元素移除后还要放回去是出于什么样的考虑,如果真存在这种考虑的情况下,我估计你选错了类型,使用的不应该是队列。几种基础的数据结构在.net类库中均有实现,比如队列(Queue),栈(Stack), 泛型(List)等等,对于链表也可以很容易地构造出来这样的类来。
相比较于标准锁来说,增加一点工作量即可提高 SpinLock 的性能。但需注意的一点是,SpinLock 比标准锁更耗费资源。您可以使用分析工具中并发分析功能,查看哪种类型的锁可以在您的程序中提供更好的性能。有关更多信息,请参见并发可视化工具。C#VBclass SpinLockDemo2 { constint N = 100000; static Queue<Data> _queue = new Queue<Data>(); staticobject _lock = new Object(); static SpinLock _spinlock = new SpinLock(); class Data { publicstring Name { get; set; } publicdouble Number { get; set; } } staticvoid Main(string[] args) { // First use a standard lock for comparison purposes. UseLock(); _queue.Clear(); UseSpinLock(); Console.WriteLine("Press a key"); Console.ReadKey(); } privatestaticvoid UpdateWithSpinLock(Data d, int i) { bool lockTaken = false; try { _spinlock.Enter(ref lockTaken); _queue.Enqueue( d ); } finally { if (lockTaken) _spinlock.Exit(false); } } privatestaticvoid UseSpinLock() { Stopwatch sw = Stopwatch.StartNew(); Parallel.Invoke( () => { for (int i = 0; i < N; i++) { UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i); } }, () => { for (int i = 0; i < N; i++) { UpdateWithSpinLock(new Data() { Name = i.ToString(), Number = i }, i); } } ); sw.Stop(); Console.WriteLine("elapsed ms with spinlock: {0}", sw.ElapsedMilliseconds); } staticvoid UpdateWithLock(Data d, int i) { lock (_lock) { _queue.Enqueue(d); } } privatestaticvoid UseLock() { Stopwatch sw = Stopwatch.StartNew(); Parallel.Invoke( () => { for (int i = 0; i < N; i++) { UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i); } }, () => { for (int i = 0; i < N; i++) { UpdateWithLock(new Data() { Name = i.ToString(), Number = i }, i); } } ); sw.Stop(); Console.WriteLine("elapsed ms with lock: {0}", sw.ElapsedMilliseconds); } } SpinLock 对于很长时间都不会持有共享资源上的锁的情况可能很有用。对于此类情况,在多核计算机上,一种有效的做法是让已阻塞的线程旋转几个周期,直至锁被释放。通过旋转,线程将不会进入阻塞状态(这是一个占用大量 CPU 资源的过程)。在某些情况下,SpinLock 将会停止旋转,以防止出现逻辑处理器资源不足的现象,或出现系统上超线程的优先级反转的情况。此示例使用 System.Collections.Generic.Queue<T> 类,这要求针对多线程的访问进行用户同步。在以.NET Framework 4 为目标的应用程序中,还可以选择使用 System.Collections.Concurrent.ConcurrentQueue<T>,这不需要任何用户锁。请注意,在对 Exit 的调用中使用了 false(在 Visual Basic 中为 False)。这样可以提供最佳性能。