Monitor.Wait初探(6)

再来加深一下印象,每一个Object实例都维护一个SyncBlock并通过这个玩意来进行线程的同步,所以Monitor.Wait最终走到这个BOOL SyncBlock::Wait(INT32 timeOut, BOOL exitContext)并不足奇。在SyncBlock内部我们维护了一个所有正在等待此同步索引块的线程的队列,那具体是通过什麽来控制的呢,通过阅读SyncBlock::Wait源码,我们知道SyncBlock内部的这个维护链表就是SLink       m_Link;

// We thread two different lists through this link.  When the SyncBlock is
// active, we create a list of waiting threads here.  When the SyncBlock is
// released (we recycle them), the SyncBlockCache maintains a free list of
// SyncBlocks here.
//
// We can't afford to use an SList<> here because we only want to burn
// space for the minimum, which is the pointer within an SLink.
SLink       m_Link;

 

在SyncBlock::Wait中通过调用ThreadQueue::EnqueueThread把当前线程的WaitEventLink加入到SyncBlock的m_Link之中:

// Enqueue is the slow one.  We have to find the end of the Q since we don't
// want to burn storage for this in the SyncBlock.
/* static */
inline void ThreadQueue::EnqueueThread(WaitEventLink *pWaitEventLink, SyncBlock *psb)
{
    LEAF_CONTRACT;
    COUNTER_ONLY(GetPrivatePerfCounters().m_LocksAndThreads.cQueueLength++);

    _ASSERTE (pWaitEventLink->m_LinkSB.m_pNext == NULL);

    SyncBlockCache::LockHolder lh(SyncBlockCache::GetSyncBlockCache());

    SLink       *pPrior = &psb->m_Link;

    while (pPrior->m_pNext)
    {
        // We shouldn't already be in the waiting list!
        _ASSERTE(pPrior->m_pNext != &pWaitEventLink->m_LinkSB);

        pPrior = pPrior->m_pNext;
    }
    pPrior->m_pNext = &pWaitEventLink->m_LinkSB;
}

 

通过分析Thread的结构,我们知道Thread的两个私有字段:

// For Object::Wait, Notify and NotifyAll, we use an Event inside the
// thread and we queue the threads onto the SyncBlock of the object they
// are waiting for.
CLREvent        m_EventWait;
WaitEventLink   m_WaitEventLink;

 

WaitEventLink是一个struct用来管理线程等待的事件,而CLREvent        m_EventWait显然就是当前用来阻塞线程或者线程用来等待的事件对象:

// Used inside Thread class to chain all events that a thread is waiting for by Object::Wait
struct WaitEventLink {
    SyncBlock      *m_WaitSB;
    CLREvent       *m_EventWait;
    Thread         *m_Thread;       // Owner of this WaitEventLink.
    WaitEventLink  *m_Next;         // Chain to the next waited SyncBlock.
    SLink           m_LinkSB;       // Chain to the next thread waiting on the same SyncBlock.
    DWORD           m_RefCount;     // How many times Object::Wait is called on the same SyncBlock.
};

 

再返回到BOOL SyncBlock::Wait(INT32 timeOut, BOOL exitContext)

我们看到刚开始就需要检查是否已经有线程在等待本SyncBlock,方法就是:

// Does this thread already wait for this SyncBlock?
   WaitEventLink *walk = pCurThread->WaitEventLinkForSyncBlock(this);

 

若果已经有了,引用数加1:

// Wait on the same lock again.
walk->m_Next->m_RefCount ++;

 

如没有,则属于第一次,需要先创建一个事件对象CLREvent,创建过程:

// First time this thread is going to wait for this SyncBlock.
       CLREvent* hEvent;
       if (pCurThread->m_WaitEventLink.m_Next == NULL) {
           hEvent = &(pCurThread->m_EventWait);
       }
       else {
           hEvent = GetEventFromEventStore();
       }

 

 

而这个事件对最后真正用来WaitForMultipleObjects的那个句柄至关重要。为什麽这麽说,我们继续看SyncBlock::Wait最后调用了pCurThread->Block(timeOut, &syncState);

// Called out of SyncBlock::Wait() to block this thread until the Notify occurs.
BOOL Thread::Block(INT32 timeOut, PendingSync *syncState)
{
    WRAPPER_CONTRACT;

    _ASSERTE(this == GetThread());

    // Before calling Block, the SyncBlock queued us onto it's list of waiting threads.
    // However, before calling Block the SyncBlock temporarily left the synchronized
    // region.  This allowed threads to enter the region and call Notify, in which
    // case we may have been signalled before we entered the Wait.  So we aren't in the
    // m_WaitSB list any longer.  Not a problem: the following Wait will return
    // immediately.  But it means we cannot enforce the following assertion:
//    _ASSERTE(m_WaitSB != NULL);

    return (Wait(syncState->m_WaitEventLink->m_Next->m_EventWait, timeOut, syncState) != WAIT_OBJECT_0);
}

 

这时候又紧接着调用了Wait(syncState->m_WaitEventLink->m_Next->m_EventWait, timeOut, syncState),第一个参数明显就是刚才的CLREvent,

// Return whether or not a timeout occured.  TRUE=>we waited successfully
DWORD Thread::Wait(CLREvent *pEvent, INT32 timeOut, PendingSync *syncInfo)
{
    WRAPPER_CONTRACT;

    DWORD   dwResult;
    DWORD   dwTimeOut32;

    _ASSERTE(timeOut >= 0 || timeOut == INFINITE_TIMEOUT);

    dwTimeOut32 = (timeOut == INFINITE_TIMEOUT
                   ? INFINITE
                   : (DWORD) timeOut);

    dwResult = pEvent->Wait(dwTimeOut32, TRUE /*alertable*/, syncInfo);

    // Either we succeeded in the wait, or we timed out
    _ASSERTE((dwResult == WAIT_OBJECT_0) ||
             (dwResult == WAIT_TIMEOUT));

    return dwResult;
}

 

而最后真正的Wait还是发生在CLREvent内部,看看它的Wait:

DWORD CLREvent::Wait(DWORD dwMilliseconds, BOOL alertable, PendingSync *syncState)
{
    WRAPPER_CONTRACT;
    return WaitEx(dwMilliseconds, alertable?WaitMode_Alertable:WaitMode_None,syncState);
}

 

再往下看就和之前的重复了,但是这里我们要着重的地方是CLREvent的私有字段

HANDLE m_handle;

其实你会发现这才是最后调用WaitForMupltipleObjectEx函数需要的那个句柄对象,而它就封装在CLREvent之中,这里的Handle就代表一个内核事件对象,

那麽那麽!这里的WaitForMupltipleObjectEx在什麽情况下返回呢?对的,需要事件对象的Set之后才能返回,ok,现在再让我们回忆一下Monitor.Wait在什麽

时候返回,没错,就是需要在其它的线程中调用Monitor.Pulse之后才能返回,这个Pulse名字起得很形象。由此,我们自然能推断出Pulse最后其实只不过是Event.Set,现在让我们看看Pulse:

void SyncBlock::Pulse()
{
    CONTRACTL
    {
        INSTANCE_CHECK;
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    WaitEventLink  *pWaitEventLink;

    if ((pWaitEventLink = ThreadQueue::DequeueThread(this)) != NULL)
        pWaitEventLink->m_EventWait->Set();
}

看到这段代码,我们再对照Monitor.Pulse的描述:从队列中取到排在最前面的线程,这里其实等价于取到那个线程的Event事件对象并Set之,由此一来,正在WaitForMupltipeObjects这个事件的线程将获得释放,对于有多个线程等待同一个Event的情况,究竟是哪个线程会被释放,还应该取决于线程的优先级等属性,但是anyway,这样的调度过程已经交给操作系统定夺了。

同理PulseAll:

void SyncBlock::PulseAll()
{
    CONTRACTL
    {
        INSTANCE_CHECK;
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
    }
    CONTRACTL_END;

    WaitEventLink  *pWaitEventLink;

    while ((pWaitEventLink = ThreadQueue::DequeueThread(this)) != NULL)
        pWaitEventLink->m_EventWait->Set();
}

posted @ 2012-03-25 10:17  Dance With Automation  Views(364)  Comments(0Edit  收藏  举报