代码实现的功能如下:一个生产者线程,一个消费者程,生产一个产品,消费一个,再生产,再消费...
A版代码如下:
        static void ProduceOneA()
        {
            lock (locker)
            {
                while (true)
                {
                    if (isHave)
                        Monitor.Wait(locker);                    Thread.Sleep(1000);
                    Console.WriteLine("生产一个");
                    isHave = true;
                    Monitor.Pulse(locker);
                }   
            }
        }        static void ConsumeOneA()
        {
            lock (locker)
            {
                while (true)
                {
                    if(!isHave)
                        Monitor.Wait(locker);                    Thread.Sleep(500);
                    Console.WriteLine("消费一个");
                    isHave = false;
                    Monitor.Pulse(locker);
                }
            }
        }        static object locker = new object();
        volatile static bool isHave = false;        static void Main(string[] args)
        {
            new Thread(ProduceOneA).Start();
            new Thread(ConsumeOneA).Start();
        }是比较常见的lock{while(){}}结构,B版代码如下:
       static void ProduceOneB()
        {
            while (true)
            {
                lock (locker)
                {
                    if (isHave)
                        Monitor.Wait(locker);
                    Thread.Sleep(1000);
                    Console.WriteLine("生产一个");
                    isHave = true;
                    Monitor.Pulse(locker);
                }
            }
        }        static void ConsumeOneB()
        {
            while (true)
            {
                lock (locker)
                {
                    if (!isHave)
                        Monitor.Wait(locker);
                    Thread.Sleep(500);
                    Console.WriteLine("消费一个");
                    isHave = false;
                    Monitor.Pulse(locker);
                }
            }
        }注意这里变成了while(){lock{}},这两个版本的代码都能达到目的,但孰优孰劣,高手给分析下,个人感觉lock{}里的同步代码应该越短约好如B版代码,但B版的结构频繁调用lock{}又是一个问题...

解决方案 »

  1.   

    lock锁的对象越小越好吧,跟代码块大小有关系吗?
      

  2.   


            void ProduceOneA() 
            { 
                    while (Interlocked.Read(ref bRun) == 1) 
                    { 
                        try
                          {    
                                ProduceHandler.waitone();                
                                Console.WriteLine("生产一个"); 
                                ConsumeHandler.set();
                           }
                          finally
                          {
                              Thread.Sleep(1000);
                          }
                    }
            }         void ConsumeOneA() 
            { 
                    while (Interlocked.Read(ref bRun) == 1) 
                    { 
                          try
                          {     ConsumeHandler.waitone();
                                Console.WriteLine("消费一个"); 
                                ProduceHandler.set();
                           }
                          finally
                          {
                              Thread.Sleep(1000);
                          }
                    } 
                } 
            }         private ReaderWriterLock m_RWHandlers = new ReaderWriterLock();
            long m_Run = 0;
            AutoResetEvent m_ConsumeHandler = new AutoResetEvent(false);
            AutoResetEvent m_ProduceHandler = new AutoResetEvent(true);
            static void Main(string[] args) 
            { 
                Interlocked.Exchange(ref m_Run, 1);
                new Thread(new threadstart(ProduceOneA)).Start(); 
                new Thread(new threadstart(ConsumeOneA)).Start(); 
            } 
      

  3.   

    LZ的代码需要LOCK的其实就只是一个isHave对象,因此只需要把对isHave的操作放在LOCK里面就可以了,就你的代码中,每一个无论是ProduceOne还是ConsumeOne,对isHave的操作都只有两个,一个是读(if(isHave/!isHave)),一个是赋值(isHave=false/true),因此你把这两个语句集合在一起,然后放在lock{}里面,这样就可以保证lock{}里面的代码量最少,而且效率也是比较高的
      

  4.   

    貌似lock就是monitor.enter方法吧 写在while里就是频繁调用这个方法了 觉得性能不是很好个人理解 不对别扔鸡蛋
      

  5.   

    看了后面的回答真是汗水
    A版本才是Monitor.Wait和Monitor.Pulse的正确用法,参考
    http://msdn.microsoft.com/zh-cn/library/system.threading.monitor.pulse.aspx
      

  6.   

    A版本是错的。示例代码中可没使用while(true)。
    lock是保护其中的代码不会同时被不同线程调用。lock块中使用while(ture)???除了第1个调用的线程,其它调用这部分代码的线程会卡在lock的地方,这段代码无法实现真正的多线程同时和多线程消费。
    你的ProduceOneA和ConsumeOneA都只有一个,lock事实上没有起没有任何作用,所以没报问题。lock块中的代码确实越少越好,不过至少包住读数据、处理数据、写数据的整个过程。
    另外lock放在循环体内,单个处理线程效率降低、多个线程同时处理同一功能的,总效率会提高。但是如果循环变量的同步处理得不好,也容易出现问题。Monitor才是起协调ProduceOneA和ConsumeOneA的生产和消费的功能的对象。
    isHave可以去掉。它的功能看上去与Monitor完全一梓,而这种用于自行控制线程同步的全局状态,会影响后续扩展代码应用的。
      

  7.   

    在类里面加一句Object m_objForLock=new Object();
    然后lock(m_objForLock)...
      

  8.   

    楼上的,有没有看过msdn:
    Monitor.Wait 方法 (Object)
    释放对象上的锁并阻止当前线程,直到它重新获取该锁。lock块中使用while(ture)怎么了?
    跑到Wait的时候,locker就被释放了
    谁说必须要等到第一个线程退出lock,其他线程才能获得locker的?
    Msdn上没有用while(true),是因为生产/消费有限个
    而A版本用while(true)是因为要无限生产/消费而已
      

  9.   

    lock (locker) 实际上和下面的代码一致:

    try 

        Monitor.Enter(locker);
        …;
    }
    finally { Monitor.Exit();}由此看来性能方面,还是把while放在lock里面好一些。
      

  10.   

    另外,是不是把
    Monitor.Pulse(locker); 
    提到if里面更清晰一些呢?变成if (isHave)
    {
       Monitor.Pulse(locker); 
       Monitor.Wait(locker);
    }因为:
    Monitor.Wait释放对象上的锁并阻止当前线程,直到它重新获取该锁(相当于先调用 Monitor.Exit 然后再调用 Monitor.Enter)。
    Monitor.Pulse通知下一个等待线程在当前线程释放锁后可以获得锁。
    所以我觉得Monitor.Pulse放在Monitor.Wait之前,代码可读性更好一些,避免在这里出bug的可能性。
      

  11.   

    我发现微软给的列子有问题
    大家可以检测一下,把微软的例子考下来运行一下,如果运气比较好的话,就会发现运行两三次就会死锁一次。
    我简单的分析了一下原因。如果SecondeThread先锁m_smplQueues,那么FirstThread就会陷入死锁的状态,因为只有等到SecondeThread执行Monitor.Wait(m_smplQueue, 1000),FirstThread才能执行,但是线程一永远收不到Monitor.Pulse(m_smplQueue);就会死锁在那里,因为SecondeThread等不到线程一调用 Monitor.Pulse(m_smplQueue);等待一秒钟后就推出了,所以FirstThread永远的只有等在哪里了。大家不要太相信微软的例子,实践才是硬道理
      

  12.   

    至于楼主的问题,我还是觉得A好,只要保证不死锁,毕竟频繁的调用lock还是很耗资源的,具体效能可以用生产消费一定一个数值,然后在比较两种方法的消耗时间。
      

  13.   

    你能写出Lock(){while (true)...}代码,我很佩服!!!
      

  14.   

    A版代码是对的,而且比B版代码效率高。Monitor对象实际上是Win32的Critical Section,Monitor.Wait实际上相当于退出了,所以A版不会死锁。而B版,在while中调用lock,会造成频繁初化始和进入Critical Section,所以效率不高。
    10楼的建议很好。
    8楼的代码比A版的代码更好,效率高而且代码清晰。我是从C++转到C#的,所以更喜欢用Mutex,Event这些和Win32近似的类,不喜欢Monitor,我觉得Monitor和lock容易使初学者犯错。
    A版、B版和8楼的代码都存在一个问题:如果在消费者消费产品之前,生产者已经生产多于1个的产品,比如说已生产了2个,则消费者只能消费第1个产品,然后就等待生产者再生产。实际上这时已经有产品了,就是第2个产品。而消费者必须等待生产品产生第3个产品时才能继续消费。也就是说,第2个产品永远不会被消费!
    这个问题可以通过将A代码的bHave改成一个计数器,或将8楼代码的bRun改成一个计数器,来解决。
      

  15.   

    A版代码是对的,而且比B版代码效率高。Monitor对象实际上是Win32的Critical Section,Monitor.Wait实际上相当于退出了Critical Section,所以A版不会死锁。而B版,在while中调用lock,会造成频繁初化始和进入Critical Section,所以效率不高。 
    10楼的建议很好。A代码加上10楼的建议就比较完美了。 
    8楼的代码比A版的代码更好,效率高而且代码清晰。我是从C++转到C#的,所以更喜欢用Mutex,Event这些和Win32近似的类,不喜欢Monitor,我觉得Monitor和lock容易使初学者犯错。 
    A版、B版和8楼的代码都存在一个问题:如果在消费者消费产品之前,生产者已经生产多于1个的产品,比如说已生产了2个,则消费者只能消费第1个产品,然后就等待生产者再生产。实际上这时已经有产品了,就是第2个产品。而消费者必须等待生产者产生第3个产品时才能继续消费。也就是说,第2个产品永远不会被消费! 
    这个问题可以通过将A代码的bHave改成一个计数器,或将8楼代码的bRun改成一个计数器,来解决。
      

  16.   

    A 版本似乎有问题, ProduceOneA线程怎么退出While循环呢, 如果没退出While循环,如何释放Locker呢。那么ConsumeOneA线程就永远没有机会进行Lock代码。
      

  17.   

    楼上的兄弟,Monitor.Wait在起作用!
      

  18.   

    有人说可以去掉isHave,不过我没找到怎么不要isHave来实际同样的功能,能贴个代码上来吗?
      

  19.   

    Monitor 类不对指示 Pulse 方法已被调用的状态进行维护。因此,如果您在没有等待线程时调用 Pulse,则下一个调用 Wait 的线程将阻止,似乎 Pulse 从未被调用过。如果两个线程正在使用 Pulse 和 Wait 交互,则可能导致死锁。将其与 AutoResetEvent 类的行为相比较:如果您通过调用 AutoResetEvent 的 Set 方法向其发送信号,在没有等待线程时,AutoResetEvent 将一直保持终止状态,直到线程调用 WaitOne、WaitAny 或 WaitAll。AutoResetEvent 释放该线程并返回到未终止状态。【msdn】