Monitor vs Pulse

仅仅依据代码,结合msdn,谈经验,基础知识自行msdn or google,ok,let's go

先看一段最简单的

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Text;
   4: using System.Threading;
   5:  
   6: namespace MonitorTest
   7: {
   8:     class Program
   9:     {
  10:         static object _lock;
  11:  
  12:         static void Main(string[] args)
  13:         {
  14:             _lock = new object();
  15:             Thread.CurrentThread.Name = "main";
  16:  
  17:             Log();
  18:  
  19:             Thread t1 = new Thread(Work);
  20:             t1.Name = "work";
  21:             t1.IsBackground = true;
  22:  
  23:             Thread t2 = new Thread(Notify);
  24:             t2.Name = "notify";
  25:             t2.IsBackground = true;
  26:  
  27:  
  28:             t1.Start();
  29:             //t2.Start();
  30:  
  31:             t1.Join();
  32:             //t2.Join();
  33:  
  34:             Log();
  35:         }
  36:  
  37:         static void Log()
  38:         {
  39:             Console.WriteLine("thread: {0}, at: {1}, state: {2}",
  40:                 Thread.CurrentThread.Name,
  41:                 DateTime.Now,
  42:                 Thread.CurrentThread.ThreadState);
  43:         }
  44:  
  45:         static void Work()
  46:         {
  47:             lock (_lock)
  48:             {
  49:                 Log();
  50:                 Monitor.Wait(_lock);
  51:                 Log();
  52:             }
  53:         }
  54:  
  55:         static void Notify()
  56:         {
  57:             lock (_lock)
  58:             {
  59:                 Thread.Sleep(1000);
  60:                 Monitor.Pulse(_lock);
  61:                 Log();
  62:             }
  63:         }
  64:     }
  65: }

可以在console中看到2行输出

o1 

程序运行到这里,无论等上1年抑或2年,还是只会看到这2行输出,也就是说线程在这里发生死锁了,为什么呢?

从代码中可以看出此时除了primary线程之外,只启动了一个新的线程(work),在这个线程里有一行关键代码:

                Monitor.Wait(_lock);

根据msdn描述:

The thread that currently owns the lock on the specified object invokes this method in order to release the object so that another thread can access it.

也就是说在lock(_lock)获取_lock对象上的锁之后(由于没有其他线程跟它抢占,Enter进入ready队列之后,就直接获取锁的使用权了),Wait这个方法反而放弃了锁的使用权,同时阻塞当前的work线程,同时也没有超时参数,因此work线程就直接休眠(进入WaitSleepJoin状态),同时在主线程中Join这个work线程时,也就一直不能返回了,所以....

现在把注释的29和32行启用,再运行,运行结果变成了

o2

可以证明以下几个结论:

  • 是Monitor.Pulse拯救了work这个线程,并且是在“精确”的1000毫秒之后
  • Monitor.Wait会释放锁的使用权 //不然Notify方法不会得到调用

同时,从运行结果中可以猜出程序运行流程

在做一个试验,将28行和29行对调位置,即:

            t2.Start();
            t1.Start();

运行结果变成了

o3

程序运行到这里又死掉了.... 为什么呢?不是已经Pulse了吗? msdn中有这样一行备注解释了原因:

The Monitor class does not maintain state indicating that the Pulse method has been called. Thus, if you call Pulse when no threads are waiting, the next thread that calls Wait blocks as if Pulse had never been called.

也就是说,调用Pulse方法时,如果ready队列中没有处于WaitSleepJoin状态的线程,则即使以后有这样的线程出现了,即使之前Pulse过n次,也是没有作用滴

由于将t2线程先执行,所以在t1线程中Work方法执行时,由于Pulse状态的不维护,导致Wait的一直等待,也就是“死锁”,更加容易证明这个理论的做法是,单独只启动t1线程,在调用Monitor.Wait()之前调用Monitor.Pulse()方法,同样会出现死锁的情况,即:

        static void Work()
        {
            lock (_lock)
            {
                Log();
                Monitor.Pulse(_lock);
                Monitor.Wait(_lock);
                Log();
            }
        }

从而可以证明Pulse状态是无法维护的

ps.这几天看SharedCache代码,作者在SharedCacheThreadPool这个类中,也是利用Monitor的Wait和Pulse方法来实现“线程池”,个人觉得逻辑有些难以维护,尤其是做单元测试有些棘手

再ps,SharedCache在我看到的当前版本(应该是已公布的版本中最新的)中,有些代码是很值得重构的,例如说SharedCacheThreadPool这个类的组织,我将它包含的几个小类: WorkRequest, ThreadInfo单独提取出来并做了简单的重构之后才显得结构上比较清晰,或许是我个人能力问题吧,maybe...

再再ps,我个人总觉得SharedCache用Monitor的Wait和Pulse方法来实现线程池有点效率问题,不过我不知道m$的线程池内部是如何设计的,只是一句牢骚 Happy

posted on 2008-06-25 19:52 micYng 阅读(1188) 评论(4)  编辑 收藏 所属分类: C# basement

评论

#1楼  2008-06-25 21:08 lexus      

把你重构的代码发出来看看哈,正在学习threadpool   回复  引用  查看    

#2楼  2008-06-25 22:50 Angel Lucifer      

Monitor.Wait 和 Monitor.Pulse 方法在效率上还是可以的。
在有限的几种场景下,它可以模拟 Mutex 和 Event ,而且它大多数时候发生在用户空间,因为不用进入内核和上下文切换,效率上有提高。

但是这两种方法太诡异了,弄的不好,死锁,极低的性能都有可能发生。

理解起来也很麻烦,CLR团队鼓捣的这玩意,有创新,但也带来极高风险。   回复  引用  查看    

#3楼  2008-06-26 02:42 PH计电导率仪 [未注册用户]

血腥大地中pulse。*   回复  引用    

#4楼 [楼主] 2008-06-26 09:57 micYng      

@lexus
其实也没有特别的重构,只是类结构做了稍许调整,部分方法重新提取而已,便于分析流程 :)   回复  引用  查看    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接:
 




导航

公告

垃圾Comment,Spam杀无赦!

<2008年6月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

统计

与我联系

搜索

 

常用链接

留言簿(8)

我参与的团队

我的标签

随笔分类

随笔档案

文章分类

相册

收藏夹

.Net Enterprise Library

.Net Remoting

ASP.NET

Blog Friends:)

C# category

C# forum&blogs

C# Toolkit

CodeSmith Usages

cPP related

Design Pattern

Opensource project

Pervious Blog

Useful tip

积分与排名

最新评论

阅读排行榜

评论排行榜