信用卡获客渠道创新:多线程中的lock,Monitor.Wait和Monitor.Pulse

来源:百度文库 编辑:偶看新闻 时间:2024/05/04 14:40:12
有CSDN的朋友问一个问题,“Lock关键字不是有获取锁、释放锁的功能吗?...为什么还需要执行Pulse?”
也有朋友有些疑点,“用lock就不要用monitor了”,“Monitor.Wait完全没必要”,“为什么Pulse和Wait方法必须从同步的代码块内调用?”

这些疑问很自然。在大部分情况下,lock确实能基本达到我们要求资源同步的目的,加上配合其他同步工具,比如事件(AutoResetEvent)等的应用,日常工作中确实没有太多机会需要用到Monitor.Wait和Pulse。不过,虽然较少机会用到,事实上Wait和Pulse跟lock完全不是一回事。他们提供了更细腻的同步功能,能达到lock作不来的功能。

为更好的回答和解释这些疑问,该帖将首先介绍Wait和Pulse的用途,通过一个简单例子逐条分析同步的过程;然后提供一个用轻量级的lock,Wait和Pulse来实现一个事件通知的实例;最后谈谈DotNet4对lock编译展开的一点有趣变化。

朋友们的原贴可以在随后的注释中找到链接。
  • gomoku
  • (IDynamicObject)
  • 等 级:
#1楼 得分:0回复于:2011-12-09 18:24:56让我们首先看看MSDN对Monitor.Wait的解释(链接见注释):
释放对象上的锁并阻止当前线程,直到它重新获取该锁。...

该解释的确很粗糙,很难理解。让我们来看看它下面的备注:
同步的对象包含若干引用,其中包括对当前拥有锁的线程的引用、对就绪队列的引用和对等待队列的引用。

这个多少还给了点东西,现在我们脑海中想像这么一幅图画:
Assembly code
         |- 拥有锁的线程lockObj->|- 就绪队列(ready queue)         |- 等待队列(wait queue)
当一个线程尝试着lock一个同步对象的时候,该线程就在就绪队列中排队。一旦没人拥有该同步对象,就绪队列中的线程就可以占有该同步对象。这也是我们平时最经常用的lock方法。
为了其他的同步目的,占有同步对象的线程也可以暂时放弃同步对象,并把自己流放到等待队列中去。这就是Monitor.Wait。由于该线程放弃了同步对象,其他在就绪队列的排队者就可以进而拥有同步对象。
比起就绪队列来说,在等待队列中排队的线程更像是二等公民:他们不能自动得到同步对象,甚至不能自动升舱到就绪队列。而Monitor.Pulse的作用就是开一次门,使得一个正在等待队列中的线程升舱到就绪队列;相应的Monitor.PulseAll则打开门放所有等待队列中的线程到就绪队列。

比如下面的程序:
C# code
class Program{    static void Main(string[] args)    {        new Thread(A).Start();        new Thread(B).Start();        new Thread(C).Start();        Console.ReadLine();    }    static object lockObj = new object();    static void A()    {        lock (lockObj)               //进入就绪队列        {            Thread.Sleep(1000);            Monitor.Pulse(lockObj);            Monitor.Wait(lockObj);   //自我流放到等待队列        }        Console.WriteLine("A exit...");    }    static void B()    {        Thread.Sleep(500);        lock (lockObj)               //进入就绪队列        {            Monitor.Pulse(lockObj);        }        Console.WriteLine("B exit...");    }     static void C()    {        Thread.Sleep(800);        lock (lockObj)               //进入就绪队列        {        }        Console.WriteLine("C exit...");    }}


从时间线上来分析:
Assembly code
T  线程A0  lock( lockObj )1  {2     //...           线程B                   线程C3     //...           lock( lockObj )       lock( lockObj )4     //...           {                     {5     //...              //...6     //...              //...7     Monitor.Pulse      //...8     Monitor.Wait       //...9     //...              Monitor.Pulse10    //...           }                     }11 }时间点0,假设线程A先得到了同步对象,它就登记到同步对象lockObj的“拥有者引用”中。时间点3,线程B和C要求拥有同步对象,他们将在“就绪队列”排队:            |--(拥有锁的线程) A            |3  lockObj--|--(就绪队列)   B,C            |            |--(等待队列)时间点7,线程A用Pulse发出信号,允许第一个正在"等待队列"中的线程进入到”就绪队列“。但由于就绪队列是空的,什么事也没有发生。时间点8,线程A用Wait放弃同步对象,并把自己放入"等待队列"。B,C已经在就绪队列中,因此其中的一个得以获得同步对象(假定是B)。B成了同步对象的拥有者。C现在还是候补委员,可以自动获得空缺。而A则被关在门外,不能自动获得空缺。            |--(拥有锁的线程) B            |8  lockObj--|--(就绪队列)   C            |            |--(等待队列)   A时间点9,线程B用Pulse发出信号开门,第一个被关在门外的A被允许放入到就绪队列,现在C和A都成了候补委员,一旦同步对象空闲,都有机会得它。            |--(拥有锁的线程) B            |9  lockObj--|--(就绪队列)   C,A            |            |--(等待队列)时间点10,线程B退出Lock区块,同步对象闲置,就绪队列队列中的C或A就可以转正为拥有者(假设C得到了同步对象)。            |--(拥有锁的线程) C            |10 lockObj--|--(就绪队列)   A            |            |--(等待队列)随后C也退出Lock区块,同步对象闲置,A就重新得到了同步对象,并从Monitor.Wait中返回...最终的执行结果就是:B exit...C exit...A exit...
  • 对我有用[1]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:如何把LPWSTR转换为 String^ 类型
  • fengyarongaa
  • ( _亚 [无党派人士])
  • 等 级:
  • 2

#2楼 得分:0回复于:2011-12-09 18:39:31

该回复于2011-12-12 09:53:16被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:★★★【分享C#源代码(数据工具和C#游戏程序)】★★★
  • gomoku
  • (IDynamicObject)
  • 等 级:
#3楼 得分:0回复于:2011-12-09 18:39:35由于Monitor.Wait的暂时放弃和Monitor.Pulse的开门机制,我们可以用Monitor来实现更丰富的同步机制,比如一个事件机(ManualResetEvent):
C# code
class MyManualEvent{    private object lockObj = new object();    private bool hasSet = false;    public void Set()    {        lock (lockObj)        {            hasSet = true;            Monitor.PulseAll(lockObj);        }    }    public void WaitOne()    {        lock (lockObj)        {            while (!hasSet)            {                Monitor.Wait(lockObj);            }        }    }}class Program{    static MyManualEvent myManualEvent = new MyManualEvent();    static void Main(string[] args)    {        ThreadPool.QueueUserWorkItem(WorkerThread, "A");        ThreadPool.QueueUserWorkItem(WorkerThread, "B");        Console.WriteLine("Press enter to signal the green light");        Console.ReadLine();        myManualEvent.Set();        ThreadPool.QueueUserWorkItem(WorkerThread, "C");        Console.ReadLine();    }    static void WorkerThread(object state)    {        myManualEvent.WaitOne();        Console.WriteLine("Thread {0} got the green light...", state);    }}

我们看到了该玩具MyManualEvent实现了类库中的ManulaResetEvent的功能,但却更加的轻便 - 类库的ManulaResetEvent使用了操作系统内核事件机制,负担比较大(不算竞态时间,ManulaResetEvent是微秒级,而lock是几十纳秒级)。

例子的WaitOne中先在lock的保护下判断是否信号绿灯,如果不是则进入等待。因此可以有多个线程(比如例子中的AB)在等待队列中排队。
当调用Set的时候,在lock的保护下信号转绿,并使用PulseAll开门放狗,将所有排在等待队列中的线程放入就绪队列,A或B(比如A)于是可以重新获得同步对象,从Monitor.Wait退出,并随即退出lock区块,WaitOne返回。随后B或A(比如B)重复相同故事,并从WaitOne返回。
线程C在myManualEvent.Set()后才执行,它在WaitOne中确信信号灯早已转绿,于是可以立刻返回并得以执行随后的命令。

该玩具MyManualEvent可以用在需要等待初始化的场合,比如多个工作线程都必须等到初始化完成后,接到OK信号后才能开工。该玩具MyManualEvent比起ManulaResetEvent有很多局限,比如不能跨进程使用,但它演示了通过基本的Monitor命令组合,达到事件机的作用。

现在是回答朋友们的疑问的时候了:
Q: Lock关键字不是有获取锁、释放锁的功能... 为什么还需要执行Pulse?
A: 因为Wait和Pulse另有用途。

Q: 用lock 就不要用monitor了(?)
A: lock只是Monitor.Enter和Monitor.Exit,用Monitor的方法,不仅能用Wait,还可以用带超时的Monitor.Enter重载。

Q: Monitor.Wait完全没必要 (?)
A: Wait和Pulse另有用途。

Q: 什么Pulse和Wait方法必须从同步的代码块内调用?
A: 因为Wait的本意就是“[暂时]释放对象上的锁并阻止当前线程,直到它重新获取该锁”,没有获得就谈不到释放。


我们知道lock实际上一个语法糖糖,C#编译器实际上把他展开为Monitor.Enter和Monitor.Exit,即:
C# code
lock(lockObj){   //...}////相当于(.Net4以前):Monitor.Enter(lockObj);try{   //...}finally{   Monitor.Exit(lockObj);}

但是,这种实现逻辑至少理论上有一个错误:当Monitor.Enter(lockObj);刚刚完成,还没有进入try区的时候,有可能从其他线程发出了Thread.Abort等命令,使得该线程没有机会进入try...finally。也就是说lockObj没有办法得到释放,有可能造成程序死锁。这也是Thread.Abort一般被认为是邪恶的原因之一。

DotNet4开始,增加了Monitor.Enter(object,ref bool)重载。而C#编译器会把lock展开为更安全的Monitor.Enter(object,ref bool)和Monitor.Exit:
C# code
lock(lockObj){   //...}////相当于(DotNet 4):bool lockTaken = false;try{    Monitor.Enter(lockObj,ref lockTaken);    //}finally{    if (lockTaken) Monitor.Exit(lockObj);}

现在Monitor.TryEnter在try的保护下,“加锁”成功意味着“放锁”将得到finally的保护。


注释和引用:
Monitor.Wait 方法
http://msdn.microsoft.com/zh-cn/library/79fkfcw1.aspx

Monitor.TryEnter 方法
http://msdn.microsoft.com/zh-cn/library/dd289679.aspx

请问,多线程Monitor类
http://topic.csdn.net/u/20111206/15/744c70de-49dc-4694-a09e-180438d7f8f0.html

请问,这个关于多线程的代码不懂
http://topic.csdn.net/u/20111208/23/64671dd4-7fdc-4d76-b3b9-1fd18087e6e0.html
  • 对我有用[1]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:26个字母组合算法--急
  • qq283868910
  • (毛飞扬)
  • 等 级:
#4楼 得分:0回复于:2011-12-09 18:54:08

该回复于2011-12-12 09:53:16被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:ASP.NET怎么防止重复提交!!
  • zgr2038079
  • (zgr2038079)
  • 等 级:
#5楼 得分:0回复于:2011-12-09 18:55:31

该回复于2011-12-12 11:48:08被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
精华推荐:"精通"C#的进来回答面试题,看自己有多“精”~~~~~~~~~`
  • chenandczh
  • (绿领巾同学)
  • 等 级:
#6楼 得分:0回复于:2011-12-09 19:04:08

该回复于2011-12-12 11:49:21被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • awayy1432
  • (oO脾气不坏Oo)
  • 等 级:
#7楼 得分:0回复于:2011-12-09 19:18:04

该回复于2011-12-11 11:33:04被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • awayy1432
  • (oO脾气不坏Oo)
  • 等 级:
#8楼 得分:0回复于:2011-12-09 19:19:44恩 讲的很清楚  
学习了.

以前就用Monitor.Wait 实现对于大量线程的阻塞 达到暂停效果
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • Sandy945
  • (阿非 (艰难困苦,玉汝于成!))
  • 等 级:
  • 2

    2

    更多勋章
#9楼 得分:0回复于:2011-12-09 19:27:59

今天回答线程问题的时候,让我有写总结的冲动,我实在是很懒。

gomoku 写了这篇文章真的是太好了。

“朋友们的疑问”那天看到的时候,真的有些无奈,那些回答会对提问者有误导。

还请楼下的朋友们不要无意义回复。想收藏可以点文章标题右侧的收藏。

同时也请gomoku把帖子挪到asp.net版,好让我有编辑的权限。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • moonwrite
  • 等 级:
#10楼 得分:0回复于:2011-12-09 20:01:50

该回复于2011-12-12 09:52:24被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • Sandy945
  • (阿非 (艰难困苦,玉汝于成!))
  • 等 级:
  • 2

    2

    更多勋章
#11楼 得分:0回复于:2011-12-09 20:08:22
C# code
static object lockObj = new object();static void A(){    lock (lockObj)               //进入就绪队列    {        //因为A所在的线程Priority高,所以会先进来        Console.WriteLine("Into A lock block");        Thread.Sleep(1000);        //这里调用了Monitor.Pulse(lockObj),会通知lockObj的等待队列        //但现在lockObj的等待队列没有线程在等待,所以通知不被处理        Monitor.Pulse(lockObj);        //自我流放到等待队列                        Monitor.Wait(lockObj);//同时此线程在此停止        //线程此时的状态是WaitSleepJoin        //虽然在Monitor.Wait之前有Monitor.Pulse通知等待队列,但是过期无效。        //无论之前有多少个Monitor.Pulse        //本线程在等待队列后接受到Monitor.Pulse的通知时,线程回到就绪队列        //从就绪队列中出来的时候,线程在此恢复     }    Console.WriteLine("A exit...");}static void B(){    lock (lockObj)               //进入就绪队列    {        //B和C都在就绪队列,因为B所在的线程Priority比C高,所以会先进来        Console.WriteLine("Into B lock block");        //这里调用了Monitor.Pulse(lockObj),会通知lockObj的等待队列        //于此同时,A 已经在等待队列了        Monitor.Pulse(lockObj);        Console.WriteLine("B Call Pulse");    }    Console.WriteLine("B lock block exit...");    Thread.Sleep(1);    Console.WriteLine("B exit...");}static void C(){    //如果CPU是低负载理想的情况,此时的就绪队列中会有两个线程    //除了C还有接收到信号的A,C排在前面理想情况会先进来 但不是绝对,有时会是A     lock (lockObj)               //进入就绪队列    {        Console.WriteLine("Into C lock block");    }    Console.WriteLine("C exit...");}static void Main(string[] args){    //设置Priority,确保按代码顺序执行    Thread a = new Thread(A);    a.Priority = ThreadPriority.Highest;    a.Start();    Thread b = new Thread(B);    b.Priority = ThreadPriority.Normal;    b.Start();    Thread c = new Thread(C);    c.Priority = ThreadPriority.Lowest;    c.Start();                 Console.ReadLine();}


最后输出的三句话 
C exit...
B exit...
A exit...

顺序不是固定的,而是要结合CPU等系统资源的调配
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • yuxuanji
  • (正义审判)
  • 等 级:
#12楼 得分:0回复于:2011-12-09 20:37:52
引用 11 楼 sandy945 的回复:

C# code

static object lockObj = new object();

static void A()
{

lock (lockObj) //进入就绪队列
{
//因为A所在的线程Priority高,所以会先进来
Console.WriteLine("Into A lock block");
……

lz只是假设A先得到lock
非常棒的技术贴,mark
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • liuyilin888
  • (liuyilin888)
  • 等 级:
#13楼 得分:0回复于:2011-12-09 20:41:19

该回复于2011-12-12 11:50:09被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • q107770540
  • ( Tim [兔子党执行督察])
  • 等 级:
  • 3

    更多勋章
#14楼 得分:0回复于:2011-12-09 22:02:50

该回复于2011-12-12 11:50:45被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • wlf535944903
  • (专注.net~)
  • 等 级:
#15楼 得分:0回复于:2011-12-09 22:09:20

该回复于2011-12-12 11:51:20被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • majin624140
  • (shengzi)
  • 等 级:
#16楼 得分:0回复于:2011-12-09 22:53:06

该回复于2011-12-12 11:52:08被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • xiven
  • (xiven)
  • 等 级:
#17楼 得分:0回复于:2011-12-09 22:54:54

该回复于2011-12-12 09:44:39被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • hyblusea
  • (C-Sharp[机器猫])
  • 等 级:
#18楼 得分:0回复于:2011-12-09 23:39:40占个位置,以后仔细学习
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • koyo22
  • (koyo22)
  • 等 级:
#19楼 得分:0回复于:2011-12-10 01:14:05非常好,
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • nimenshishabi
  • (vsdyCOM)
  • 等 级:
#20楼 得分:0回复于:2011-12-10 02:03:39楼主恩细心啊,学习哦
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • zhan7505201
  • (zhan7505201)
  • 等 级:
#21楼 得分:0回复于:2011-12-10 07:19:42

该回复于2011-12-12 09:42:45被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • mrlen
  • (林别卓)
  • 等 级:
#22楼 得分:0回复于:2011-12-10 08:10:38神贴。收藏看。还没用这么复杂的
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • liveths
  • (liveths)
  • 等 级:
#23楼 得分:0回复于:2011-12-10 08:42:48

该回复于2011-12-12 09:41:26被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • Change_L
  • (Change_L)
  • 等 级:
#24楼 得分:0回复于:2011-12-10 08:54:07

该回复于2011-12-12 09:41:26被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • starfd
  • (回家抱孩子~~~~)
  • 等 级:
#25楼 得分:0回复于:2011-12-10 08:56:17

该回复于2011-12-12 13:44:30被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • startstartsvip
  • 等 级:
#26楼 得分:0回复于:2011-12-10 09:23:28

该回复于2011-12-12 09:41:27被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • josephSC
  • (JosephSC)
  • 等 级:
#27楼 得分:0回复于:2011-12-10 09:33:50

该回复于2011-12-12 09:41:28被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • ws_hgo
  • (蓝天白云--(全面提升!!))
  • 等 级:
#28楼 得分:0回复于:2011-12-10 09:46:45

该回复于2011-12-12 09:40:20被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • huyong147
  • (huyong147)
  • 等 级:
#29楼 得分:0回复于:2011-12-10 10:20:04

该回复于2011-12-12 09:40:32被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • imeylp
  • (imeylp)
  • 等 级:
#30楼 得分:0回复于:2011-12-10 11:10:51

该回复于2011-12-12 09:38:46被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • imeylp
  • (imeylp)
  • 等 级:
#31楼 得分:0回复于:2011-12-10 11:16:41

该回复于2011-12-11 11:23:05被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • sdfkfkd
  • (特别(男))
  • 等 级:
#32楼 得分:0回复于:2011-12-10 12:32:13
引用 12 楼 yuxuanji 的回复:

引用 11 楼 sandy945 的回复:

C# code

static object lockObj = new object();

static void A()
{

lock (lockObj) //进入就绪队列
{
//因为A所在的线程Priority高,所以会先进来
Console.WriteLine("Into A lock……

+1
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • xiaoyue_0913
  • (milering)
  • 等 级:
#33楼 得分:0回复于:2011-12-10 12:48:09学习了,以前没有了解的这么细
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • yanbuodiao
  • (烟波钓)
  • 等 级:
#34楼 得分:0回复于:2011-12-10 13:07:06

该回复于2011-12-12 09:37:56被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • snowmagic
  • (sl)
  • 等 级:
#35楼 得分:0回复于:2011-12-10 13:31:14写的挺好
不过你把顺序搞反了吧,monitor是win32的概念,而lock是c#的语法
应该是先有monitor再有lock
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • yuan521929
  • (愚知)
  • 等 级:
#36楼 得分:0回复于:2011-12-10 13:41:33介绍的到位 
  顶!
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lux7114321
  • (lux7114321)
  • 等 级:
#37楼 得分:0回复于:2011-12-10 14:09:31恩 讲的很清楚  
学习了
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • xienb
  • (xienb)
  • 等 级:
#38楼 得分:0回复于:2011-12-10 16:58:00恩, 来学习下 ,希望有分 谢谢
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • kingmax54212008
  • (Jimmy Huang)
  • 等 级:
#39楼 得分:0回复于:2011-12-10 17:30:04收下,慢慢分析一下 ~~~
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • zanfeng
  • (qq:1060151476)
  • 等 级:
#40楼 得分:0回复于:2011-12-10 17:44:21技术贴值得推荐。顶顶。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • yanghe1117
  • 等 级:
#41楼 得分:0回复于:2011-12-10 18:16:39好贴,支持
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lijianli9
  • (清秋)
  • 等 级:
#42楼 得分:0回复于:2011-12-10 18:35:16讲解的好
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • dongxinxi
  • (夕阳西下,断肠人在跳崖)
  • 等 级:
#43楼 得分:0回复于:2011-12-10 19:25:14

该回复于2011-12-12 09:27:30被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lizhibin11
  • (lizhibin11)
  • 等 级:
#44楼 得分:0回复于:2011-12-10 19:40:38经测试,AutoResetEvent的效率和Monitor.Wait几乎一样,有心人可以测试对比一下。
Monitor.Enter等待时间如果很长,也是会进入内核态的,它对应的是win32的CRITICAL_SECTION结构。但是Monitor.Wait对应哪个win32 api我没找到,或者它是.net新创了一个以前win32所没有的东西,我怀疑Monitor.Wait也会进入内核态,在这点上和WaitHandle实质相同。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lmc158
  • (成功)
  • 等 级:
#45楼 得分:0回复于:2011-12-10 20:56:29就绪队列。而Monitor.Pulse的作用就是开一次门,使得一个正在等待队列中的线程升舱到就绪队列;相应的Monitor.PulseAll则打开门放所有等待队列中的线程到就绪队列。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • zmhhzmhhzmhh
  • (zmhhzmhhzmhh)
  • 等 级:
#46楼 得分:0回复于:2011-12-10 23:59:32收下了
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • chongjingsky
  • (小欤)
  • 等 级:
#47楼 得分:0回复于:2011-12-11 00:04:05

该回复于2011-12-12 09:17:36被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • xl_0715
  • (食以民为天)
  • 等 级:
#48楼 得分:0回复于:2011-12-11 00:20:11

该回复于2011-12-12 09:17:37被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • supercnsky
  • (supercnsky)
  • 等 级:
#49楼 得分:0回复于:2011-12-11 01:12:06

该回复于2011-12-12 09:17:38被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • gomoku
  • (IDynamicObject)
  • 等 级:
#50楼 得分:0回复于:2011-12-11 09:58:22如可行,也烦请斑竹在原文(3楼)中改正:
错误更正:

1、是有关开销:
(不算竞态时间,ManulaResetEvent是微秒级,而lock是几十纳秒级)。注:非竞态下的比较不能算公平;而多线程下却很难进行比较,特别是掺杂了系统调度的因素和线程切换的开销。

2、是有关lock的编译展开:
DotNet4开始,增加了Monitor.Enter(object,ref bool)重载。而C#编译器会把lock展开为更安全的Monitor.Enter(object,ref bool)和Monitor.Exit:
C# code
lock(lockObj){   //...}////相当于(DotNet 4):bool lockTaken = false;try{    Monitor.Enter(lockObj,ref lockTaken);    //}finally{    if (lockTaken) Monitor.Exit(lockObj);}
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • flyerwing
  • (80年代的内裤)
  • 等 级:
#51楼 得分:0回复于:2011-12-11 10:44:17看看大虾的研究成果。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • huangwenquan123
  • (S tar。[兔子党理事])
  • 等 级:
#52楼 得分:0回复于:2011-12-11 11:10:57

该回复于2011-12-12 09:14:53被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • ximenwuji
  • (西门无极)
  • 等 级:
#53楼 得分:0回复于:2011-12-11 11:33:22

该回复于2011-12-12 09:14:54被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lizhibin11
  • (lizhibin11)
  • 等 级:
#54楼 得分:0回复于:2011-12-11 12:13:46我查了半天Monitor.Wait的源码,到了ObjWait(bool exitContext, int millisecondsTimeout, object obj)这里就终止了,无法看到他的具体实现。不过我还是查到了一篇文章,这篇文章本身不用看,看下面的评论很有意思。
http://www.cnblogs.com/cn_wpf/archive/2007/07/20/825059.html
其中一条评论:
“internal call的实现在CLR的虚拟机实现(sscli20\clr\src\vm)里, 但不仅仅是这个文件夹, clr\src中除了bcl文件夹都不是托管代码
Monitor.Wait的实现->Monitor.ObjWait是个internal call方法, 可以在ecall.h中搜索到FCFuncElement("ObjWait", ObjectNative::WaitTimeout), 所以其实它其实是调用了ObjectNative::WaitTimeout, 找到这个函数实现往下挖X层之后你就能在synch.cpp里找到的CLREventWaitHelper函数中找到Win32API的调用:WaitForSingleObjectEx”

AutoResetEvent实际调用的也是WaitForSingleObject,那么想象一下,如果Monitor.Wait和AutoResetEvent真的有所区别的话,也许Monitor.Wait会在进入内核态之前自旋一小段时间,如果线程操作处理的足够快,也许不会进入内核态,但这只是想象,希望能得到证实。注意这里只说Monitor.Wait,因为Monitor.Enter可以确认会先自旋。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • nj_dobetter
  • (android-cn)
  • 等 级:
#55楼 得分:0回复于:2011-12-11 18:36:58俺是来看多线程同步、互斥的,没想到是ASP的技术。
不过无论是C++,Java,还是ASP,多线程的基础设施应该在抽象层上差不多
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • ryktktyktjhjh
  • (ryktktyktjhjh)
  • 等 级:
#56楼 得分:0回复于:2011-12-11 20:22:11

该回复于2011-12-12 10:58:20被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • svcce
  • (svcce)
  • 等 级:
#57楼 得分:0回复于:2011-12-11 22:14:49

该回复于2011-12-12 09:04:15被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • asd1308855
  • (asd1308855)
  • 等 级:
#58楼 得分:0回复于:2011-12-11 22:22:05

该回复于2011-12-12 09:04:16被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • YELLAN
  • 等 级:
#59楼 得分:0回复于:2011-12-11 23:02:01

该回复于2011-12-12 09:03:26被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • Sandy945
  • (阿非 (艰难困苦,玉汝于成!))
  • 等 级:
  • 2

    2

    更多勋章
#60楼 得分:0回复于:2011-12-12 02:52:22技术分享是希望能够帮助更多需要帮助的人,如果大家有与本帖内容相关的疑问可以提出来,LZ有时间肯定会做出解答的,大家也可以一起集思广益。如果有人能分享心得体会那更是求之不得。
所以还请大家不要无意义回复,试想你对此类问题有疑问,却翻了好几页没找到关键的部分,满篇的灌水什么心情,如果你觉得本文对你帮助很大,想查阅又怕记忆繁琐可以点击标题右侧的收藏。
本回复之前的无意义回复,都会通知回复人之后删除,由于精力有限本回复之后的无意义回复删除恕不另行通知。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • caozhy
  • (cfx)
  • 等 级:
  • 2

    4

    更多勋章
#61楼 得分:0回复于:2011-12-12 04:31:10不好意思,我说一句废话,阿非删除不用通知我了。

我就想说,这个贴是昨天吃早餐的时候方兴和我提到的。所以我一定要瞻仰下。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • telankes2000
  • 等 级:
#62楼 得分:0回复于:2011-12-12 09:15:38

该回复于2011-12-12 09:19:35被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • jhdxhj
  • (jhdxhj)
  • 等 级:
#63楼 得分:0回复于:2011-12-12 09:52:23非常好,恩,原来是这样啊 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • Mpt_hi
  • (月光下的土豆)
  • 等 级:
#64楼 得分:0回复于:2011-12-12 09:58:48收藏备用
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • zxj828282
  • 等 级:
#65楼 得分:0回复于:2011-12-12 10:32:30

该回复于2011-12-12 10:36:58被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • ch_zjy
  • (ch_zjy)
  • 等 级:
#66楼 得分:0回复于:2011-12-12 11:56:47

该回复于2011-12-12 11:57:36被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • ohkuy
  • (Null)
  • 等 级:
#67楼 得分:0回复于:2011-12-12 12:11:56神帖啊,
以前没注意这么细节化的东西,
讲得很好,很细,
受教了
希望先推荐,再置顶,
要不是我眼神好,差点忽略了
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • andyalex
  • (andy)
  • 等 级:
#68楼 得分:0回复于:2011-12-12 13:26:02非常好的帖子,温故一下,感谢楼主分享
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lizhibin11
  • (lizhibin11)
  • 等 级:
#69楼 得分:0回复于:2011-12-12 13:47:18Windows Vista新增了条件变量,这样在使用CRITICAL_SECTION关键代码段期间,可以调用SleepConditionVariableCS来放弃锁的所有权并阻止线程,直到被另一线程调用WakeConditionVariable唤醒,重新尝试进入临界区。这个和Monitor的所有方法都可以一一对应了,也意味着Monitor.Wait在Vista之前与之后的操作系统上应该有不同的表现。
“条件变量在某些情况中也可能更高效。SleepConditionVariableCS 和 SleepConditionVariableSRW API 会尽可能候试图避免进入内核模式的行程。但不使用条件变量的示例在调用 WaitForSingleObject 时,总是会发生至少一次内核模式往返。”
http://msdn.microsoft.com/zh-cn/magazine/cc163405.aspx
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • youbl
  • (水边)
  • 等 级:
#70楼 得分:0回复于:2011-12-12 13:53:08用Reflecor看了下4.0里的Monitor.Enter(object obj, ref bool lockTaken)方法
最后是调用:
[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical]
private static extern void ReliableEnter(object obj, ref bool lockTaken);
 
想请教下,这个ReliableEnter方法能确保加锁obj,并对lockTaken赋值为ture这2个操作是原子的吗?
而不会出现在加锁后,线程Abort,没来得及对lockTaken赋值?
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • xuStanly
  • (楼主深肖朕躬)
  • 等 级:
#71楼 得分:0回复于:2011-12-12 14:37:37好棒的帖子.
记号.
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • lizhibin11
  • (lizhibin11)
  • 等 级:
#72楼 得分:0回复于:2011-12-12 14:40:10
引用 70 楼 youbl 的回复:

用Reflecor看了下4.0里的Monitor.Enter(object obj, ref bool lockTaken)方法
最后是调用:
[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical]
private static extern void ReliableEnter(object obj, ref bool……

finally块会在调用Abort后线程中止前执行(.net 1.0和1.1例外),看50楼代码。
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • gomoku
  • (IDynamicObject)
  • 等 级:
#73楼 得分:0回复于:2011-12-12 14:46:32
引用 70 楼 youbl 的回复:
...这个ReliableEnter方法能确保加锁objj,并对lockTaken赋值为ture这2个操作是原子的吗

这是个好问题,的确两个操作必须在一个原子操作内完成。
可惜MethodImplOptions.InternalCall表明这个函数由CLR来负责实现,我们不能直接检查源码。
我选择相信微软会正确实现,他的名字ReliableEnter(Reliable: 稳妥)也暗示了实现方式。