我想做一个 LockFree Queue,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;namespace MutiThreadSynchronizationTest2
{
    class Program
    {
        //test2 used 
        static void Main(string[] args)
        {
            Producer producer = new Producer();
            Customer customer = new Customer();
            Thread produceThread = new Thread(new ThreadStart(producer.Produce));
            Thread consumeThread = new Thread(new ThreadStart(customer.Consume));            Console.WriteLine("start\n");            DateTime startTime = DateTime.Now;            produceThread.Start();            Thread.Sleep(50);
            consumeThread.Start();
            produceThread.Join();
            consumeThread.Join();            produceThread.Abort();
            consumeThread.Abort();            DateTime endTime = DateTime.Now;
            TimeSpan ts = endTime - startTime;            Console.WriteLine("Used" + ts.TotalMilliseconds);            Console.ReadLine();
        }    }    public static class ProductQueue
    {
        public static Queue<Product> productQueue = new Queue<Product>();
    }
    public class Product
    {
        public Product(string content)
        {
            Content = content;
        }        private string _content;
        public string Content
        {
            get
            {
                return _content;
            }            set
            {
                _content = value;
            }
        }
        public override string ToString()
        {
            return Content;
        }    }
    public class Producer
    {
        public void Produce()
        {
            for (int count = 0; count < 5000; count++)
            {
                // doing something;
                Thread.Sleep(5);                Random ra = new Random();
                int c = ra.Next(2, 6);
                //enqueue 1-5 products
                for (int i = 1; i < c; i++)
                {
                    if (ProductQueue.productQueue.Count > 2)
                    {
                        ProductQueue.productQueue.Enqueue(new Product(i.ToString()));
                    }
                    else
                    {
                             lock (ProductQueue.productQueue)
                        {                            ProductQueue.productQueue.Enqueue(new Product(i.ToString()));
                        }
                    }
                }            }
        }
    }
    public class Customer
    {
        public void Consume()
        {
            while (true)
            {
                //system is doint something else
                Thread.Sleep(2);
                if (ProductQueue.productQueue.Count > 0)
                {
                    if (ProductQueue.productQueue.Count > 2)
                    {
                        ProductQueue.productQueue.Dequeue();
                    }
                    else
                    {
                        lock (ProductQueue.productQueue)
                        {
                            ProductQueue.productQueue.Dequeue();
                        }
                    }
                }
                else
                {
                    break;
                }
            }
        }
    }}
这段代码 经常会出现ArgumentException异常
“源数组长度不足。请检查 srcIndex 和长度以及数组的下限。”
我的想法是,如果Queue里面的Item大于1个,按理来说EnQueue和DeQueue就可以同时进行不需要Lock了啊。
为什么会出错呢?期待原理性的回答,说一定要用Lock不给分。我就是纯粹蛋痛钻牛角尖一下而已

解决方案 »

  1.   

    Queue不是线程安全的。
    lock free queue
    http://www.google.com.hk/search?hl=zh-CN&source=hp&biw=1436&bih=709&q=lock+free+queue&btnG=Google+%E6%90%9C%E7%B4%A2&aq=f&aqi=&aql=&oq=
      

  2.   

    ArgumentException错误很奇怪,消费速度快,最有可能的错误是Dequeue一个空队列,但也不是参数错误,我复制代码跑了几次,没有异常
      

  3.   

    楼主你的例子,我发觉不需要lock。
    Produer不是只管往queue里填数据吗?那要Lock它干吗?
    Customer就是要取出数据。你在while里有判断
    if (ProductQueue.productQueue.Count > 0)
    {
        //dequeue
    }
    else
    {
        break;
    }
    也不需要lock
    你这里只有一个消费者。
      

  4.   


     对的,我也是这个想法,现在问题可能就归结在Queue内部实现不支持多线程吧?我用LinkedList实现个去queue试下
      

  5.   

    这么乱的程序,也没个简短的目标描述,里边的“.Count>2”莫名其妙,唉.......既然这么多回帖说到了lock,就说一句。其实很简单,Queue的Enqueue的Dequeue操作当然是线程安全的,这个在msdn中有很明确的说明。这无需lock。但是你的问题根本不是处在Enqueue和Dequeue操作本身,而是处在.Count属性操作。你在一个线程中取了.Count属性,难道此时就要禁止Queue进行Enqueue和Dequeue操作?不禁止,那么你取出.Count之后,当然这很快就是脏数据了啊!再看到lz的话“如果Queue里面的Item大于1个,按理来说EnQueue和DeQueue就可以同时进行不需要Lock了啊。
    为什么会出错呢?期待原理性的回答,说一定要用Lock不给分。我就是纯粹蛋痛钻牛角尖一下而已”,其实这就无法让人很好地回答你的问题,以为你自己把别人的真正回答给否定了。
      

  6.   

    “按理来说EnQueue和DeQueue就可以同时进行不需要Lock了啊”这个根本无意义。因为你是先取出了.Count属性,之后才会因为Dequeue操作而使得.Count其实已经比你之前取出地小了。队列的EnQueue和DeQueue是线程安全的,这没有问题!
      

  7.   

    哦我明白lz为什么写出这么乱得让人不想读的程序了,大概就是纠结于“期待原理性的回答,说一定要用Lock不给分。我就是纯粹蛋痛钻牛角尖一下而已”这个固执的想法所以才越写越烂,以至于写出                Thread.Sleep(2);
                    if (ProductQueue.productQueue.Count > 0)
                    {
                        if (ProductQueue.productQueue.Count > 2)
                        {[code=C#][/code]这样的程序吧!呵呵,如果你知道一旦“if (ProductQueue.productQueue.Count > 0)”这条语句执行完,下一条语句执行之前此时.Count就可能为0,你就不会写出太烂的、过分拼凑式的无意义代码了。
      

  8.   

    看看MSDN的解释吧,不多说了,希望楼主不要被某些人误导。。Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.A Queue<T> can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
      

  9.   

    不用lock也是可以的,但是也不能直接用Queue,要自己构造一个队列,然后声明一个32位或者64位的整数,该整数高位表示队列中数组的写入位置,低位表示队列中数组的读取位置,每次写入或者读取时,对该整数进行interlocked原子操作。
      

  10.   

    唉,难道中国话就这么难懂么?Queue是线程安全的,是指人家Enqueue、Dequeue两操作并行发生时不需要lock,因为这两个操作内部已经是互斥的了。而如果你一个线程读取了.Count属性(比如是10),然后另外一个线程从Queue上取下一个对象,那么这时候你再读取.Count就应该是9了,如果你不重新读取.Count属性而用原来的10作为控制,肯定要出队列长度错误的问题。不管你是使用Queue还是自己写什么结构,你在读取一个数据集合的元素个数之后,其它线程会因为改变了数据集合而使数据集合元素个数减小(或者增大)。这根本不能想当然地归结为“用或者不用Queue”的问题,这是一个很基本的线程编程问题,编写任何线程程序都要有此概念。根本不是Queue这个东西出的问题!
      

  11.   

    所谓线程安全的,是说它自身的操作在并行时是互斥的。比如.Count这个属性的内部,就会跟Enqueue、Dequeue互斥,这就是线程安全。不要简单地读一下msdn上的几个词。msdn是很文绉绉的官话,它不会像我这样仅仅针对个别问题来回答。msdn上一会说说它是队列对象线程安全的,一会儿说顺序访问它时不时线程安全的,你说它是线程安全的还是不安全的?不要两边都占!不要简单背诵msdn上的文字,应该深入地分析一下!
      

  12.   

    Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
    MSDN讲的很清楚,queue的静态方法是线程安全的,实例方法是非线程安全的,看不懂说了那么多,再说什么,能不能先把原理搞明白再说,给你看看Queue的Enqueue 和Dequeue的实现源码,如果能看出来哪里是线程安全的话,那就没必要再沟通了public void Enqueue(T item)
    {
        if (this._size == this._array.Length)
        {
            int capacity = (int) ((this._array.Length * 200L) / 100L);
            if (capacity < (this._array.Length + 4))
            {
                capacity = this._array.Length + 4;
            }
            this.SetCapacity(capacity);
        }
        this._array[this._tail] = item;
        this._tail = (this._tail + 1) % this._array.Length;
        this._size++;
        this._version++;
    }public T Dequeue()
    {
        if (this._size == 0)
        {
            ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
        }
        T local = this._array[this._head];
        this._array[this._head] = default(T);
        this._head = (this._head + 1) % this._array.Length;
        this._size--;
        this._version++;
        return local;
    }   
      

  13.   

    不是说它没有写lock语句就不是线程安全的。你给举个例子,Enqueue、Dequeue两个操作并发执行,怎么会发生冲突?
      

  14.   

    线程不安全,就是比如说程序中前一条语句还以为 this._array[this._tail] 有值,下面再去访问它就因为没有值而发生bug了,所以这是不安全的。Queue的这两个操作,怎么看出是线程不安全的?
      

  15.   

    按楼上的思路写了如下代码,有兴趣拿去跑一下,是否会出错,如果不知道什么是线程安全,就不要出来误导别人,扯远了,此贴不会再回复了。
    class Program
        {
            static void Main(string[] args)
            {            _IntQueue = new Queue<int>();
                _IntQueue.Enqueue(1);
                Thread t1 = new Thread(new ThreadStart(Enqueue));
                t1.Start();
                Thread t2 = new Thread(new ThreadStart(Dequeue));
                t2.Start();
                Console.ReadLine();        }
            static void Enqueue()
            {
                while (true)
                {
                    _IntQueue.Enqueue(DateTime.Now.Millisecond);
                }
            }        static void Dequeue()
            {
                while (true)
                {
                    _IntQueue.Dequeue();
                }
            }
            static Queue<int> _IntQueue;
        }
      

  16.   

    但是 queue是线程不安全的吧,我那段代码 不用判断 一个enqueue一个dequeue也会出错的