zoukankan      html  css  js  c++  java
  • Monitor.Wait初探(8)

    打开windbg,哦,不对,先把之前的示例程序改一下,如下,我的目的是为了调试获得Monitor.Enter在进入锁对象并等待之的处理逻辑,所以第一个线程率先拥有了锁对象,但是我们看到第一个线程sleep了太长时间(别学我,我只是为了调试Enter方法),从而导致而第二个线程会长时间的进入徘徊等待的状态,重点就是这第二个线程:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;

    namespace ConsoleApplication1
    {
        class Program
        {
            private static object _lockObject = new object();
            static void Main(string[] args)
            {
                Thread tr1 = new Thread(ThreadProc1);
                Thread tr2 = new Thread(ThreadProc2);
                tr2.Name = "TR2";
                tr1.Start();
                tr2.Start();
                //tr1.Join();
                //tr2.Join();
                Console.ReadKey();
            }

            static void ThreadProc1()
            {
                lock(_lockObject)
                {
                    Thread.Sleep(1000000);
                }
            }

            static void ThreadProc2()
            {
                try
                {
                    Monitor.Enter(_lockObject);

                }
                catch
                {

                }
                finally
                {
                    Monitor.Exit(_lockObject);
                }

            }
        }
    }

    运行改程序,attach之,

    !threads

    ~4s

    发现4号线程的托管调用栈:

    0:004> !clrstack
    OS Thread Id: 0xf30 (4)
    ESP       EIP    
    00f2f6f0 7c92e514 [GCFrame: 00f2f6f0]
    00f2f7c0 7c92e514 [HelperMethodFrame_1OBJ: 00f2f7c0] System.Threading.Monitor.Enter(System.Object)
    00f2f818 00d20269 ConsoleApplication1.Program.ThreadProc2()
    00f2f840 792d6e46 System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
    00f2f84c 792e02cf System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
    00f2f864 792d6dc4 System.Threading.ThreadHelper.ThreadStart()
    00f2fa8c 79e71b4c [GCFrame: 00f2fa8c]

    对头,这就是第二个线程ThreadProc2,调用kb查看其Native栈:

    0:004> kb2000
    ChildEBP RetAddr  Args to Child             
    00f2f45c 7c92df4a 7c809590 00000001 00f2f488 ntdll!KiFastSystemCallRet
    00f2f460 7c809590 00000001 00f2f488 00000001 ntdll!ZwWaitForMultipleObjects+0xc
    00f2f4fc 79fccf9a 00000001 001ada50 00000000 KERNEL32!WaitForMultipleObjectsEx+0x12c
    00f2f564 79fccbc7 00000001 001ada50 00000000 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT+0x6f
    00f2f584 79fcccd0 00000001 001ada50 00000000 mscorwks!Thread::DoAppropriateAptStateWait+0x3c
    00f2f608 79fccd65 00000001 001ada50 00000000 mscorwks!Thread::DoAppropriateWaitWorker+0x13c
    00f2f658 79fccee9 00000001 001ada50 00000000 mscorwks!Thread::DoAppropriateWait+0x40
    00f2f6b4 79e7549a ffffffff 00000001 00000000 mscorwks!CLREvent::WaitEx+0xf7
    00f2f6c8 79fd774d ffffffff 00000001 00000000 mscorwks!CLREvent::Wait+0x17
    00f2f754 79f016f0 001ad1f0 ffffffff 001ad1f0 mscorwks!AwareLock::EnterEpilog+0x8c
    00f2f770 79f01674 347205ba 00000000 01333664 mscorwks!AwareLock::Enter+0x61
    00f2f810 00d20269 00f2f844 792d6e46 00000000 mscorwks!JIT_MonEnterWorker_Portable+0xb3
    WARNING: Frame IP not in any known module. Following frames may be wrong.
    00f2f838 792d6e46 0133375c 00f2f858 792e02cf 0xd20269
    00f2f844 792e02cf 00f2f89c 0133375c 013336bc mscorlib_ni+0x216e46
    00f2f858 792d6dc4 013336bc 00000000 001ad1f0 mscorlib_ni+0x2202cf
    00f2f870 79e71b4c 7c98fd90 00150608 00f2f900 mscorlib_ni+0x216dc4
    00f2f880 79e8968e 00f2f950 00000000 00f2f920 mscorwks!CallDescrWorker+0x33
    00f2f900 79e96d11 00f2f950 00000000 00f2f920 mscorwks!CallDescrWorkerWithHandler+0xa3
    00f2fa38 79e96d44 7924290c 00f2fb94 00f2facc mscorwks!MethodDesc::CallDescr+0x19c
    00f2fa54 79e96d62 7924290c 00f2fb94 00f2facc mscorwks!MethodDesc::CallTargetWorker+0x1f
    00f2fa6c 79f88387 00f2facc 347201fe 001ad1f0 mscorwks!MethodDescCallSite::CallWithValueTypes+0x1a
    00f2fc54 79e9caff 00f2fdd0 00000000 00000000 mscorwks!ThreadNative::KickOffThread_Worker+0x192
    00f2fc68 79e9ca9b 00f2fd44 00f2fcf0 79fbb3cb mscorwks!Thread::DoADCallBack+0x32a
    00f2fcfc 79e9c9c1 00f2fd44 34720092 00000000 mscorwks!Thread::ShouldChangeAbortToUnload+0xe3
    00f2fd38 79e9cb4d 00f2fd44 00000001 00000000 mscorwks!Thread::ShouldChangeAbortToUnload+0x30a
    00f2fd60 79f88158 00000001 79f8826d 00f2fdd0 mscorwks!Thread::ShouldChangeAbortToUnload+0x33e
    00f2fd78 79f88232 00000001 79f8826d 00f2fdd0 mscorwks!ManagedThreadBase::KickOff+0x13
    00f2fe14 79f0e255 001accd0 b0c09b18 87ea9f98 mscorwks!ThreadNative::KickOffThread+0x269
    00f2ffb4 7c80b729 001ab9b8 00000000 00000000 mscorwks!Thread::intermediateThreadProc+0x49
    00f2ffec 00000000 79f0e20f 001ab9b8 00000000 KERNEL32!BaseThreadStart+0x37

    重点看横线部分,果然印证之前的纯代码review,最终还是调用WaitForXXXXObject函数等待事件对象的信号。

    网上的参考摘要:

    1.

    http://blogs.msdn.com/b/junfeng/archive/2004/02/18/75454.aspx

    How is lock keyword of C# implemented?

    This question is asked in an internal discussion. And here is the answer from CLR team.

    From:
    Subject: RE: How is lock keyword of C# implemented?

    At the core, it’s typically one „lock cmpxchg“ instruction (for x86) for entry, and one for exit, plus a couple dozen other instructions, all in user mode. The lock prefix is replaced with a nop on uniprocessor machines.

    The “lock cmpxchg” instruction basically stores the locking thread’s id in the object header, so another thread that tries to lock the same object can see that it’s already locked.

    The actual implementation is a lot more complicated, of course – we use the object header for other purposes, for example, so this must be detected and dealt with, plus when a thread leaves the lock, we must detect whether other threads are waiting and so on…

    Thanks

    2.

    http://software.intel.com/en-us/articles/implementing-scalable-atomic-locks-for-multi-core-intel-em64t-and-ia32-architectures/

    Implementing Scalable Atomic Locks for Multi-Core Intel® EM64T and IA32 Architectures

    The two most popular methods of locking on the Microsoft Windows platform are WaitForSingleObject and EnterCriticalSection. WaitForSingleObject is an overloaded Microsoft API which can be used to check and modify the state of a number of different objects such as events, jobs, mutexes, processes, semaphores, threads, or timers. One disadvantage of WaitForSingleObject is that it will always obtain a kernel lock, so it enters privileged mode (ring 0) whether the lock is achieved or not. This API also enters the Windows kernel even if a 0 timeout is specified. Another disadvantage of this method of locking is that it can only handle 64 threads attempting to place a lock on an object at once. The advantage of WaitForSingleObject is that it can be processed globally, which enables this API to be used for synchronization between processes. It also has the advantage of giving the OS knowledge of the locking object allowing for fairness and priority inversion.
    EnterCriticalSection can be used by putting an EnterCriticalSection and LeaveCriticalSection API call surroundin g the critical section code. This API has the advantage over WaitForSingleObject in that it will not enter the kernel unless there is contention on the lock. If there is no contention on the lock, then the API will obtain the lock in the user space and return without entering privileged mode. If there is contention, then it will follow very similar paths as WaitForSingleObject within the kernel. Under circumstances of low contention EnterCriticalSection is a much cheaper lock since it does not enter the kernel.

  • 相关阅读:
    Docker容器部署 Nginx服务
    trap 的用法 /etc/init.d/rcS trap :1 2 3 24
    android system setup and building (2)
    ubuntu 中建立tftp 服务器
    Mount nfs 报错Protocol not supported
    android system setup and building (1)
    mknod 详解
    linux 内核中已经定义的主设备号及查看设备设备号
    fstab 文件详解
    Linux根文件系统的挂载过程分析
  • 原文地址:https://www.cnblogs.com/dancewithautomation/p/2416642.html
Copyright © 2011-2022 走看看