2014-01-18 137 views
1

我发现一些声称能够使线程在准确的时间内睡眠的代码。测试代码,它似乎很好,但它在短时间后总是死锁。EnterCriticalSection死锁

这是原始代码。我在进入和离开关键部分之前放置了相片,并且看到它有时会连续两次离开或进入。它在Wait函数内的EnterCriticalSection调用中似乎陷入死锁。

有没有一种方法可以修改此代码以保留其功能而不会造成死锁?

//---------------------------------------------------------------- 
class PreciseTimer 
{ 
public: 
    PreciseTimer() : mRes(0), toLeave(false), stopCounter(-1) 
    { 
     InitializeCriticalSection(&crit); 
     mRes = timeSetEvent(1, 0, &TimerProc, (DWORD)this, 
          TIME_PERIODIC); 
    } 
    virtual ~PreciseTimer() 
    { 
     mRes = timeKillEvent(mRes); 
     DeleteCriticalSection(&crit); 
    } 

    /////////////////////////////////////////////////////////////// 
    // Function name : Wait 
    // Description  : Waits for the required duration of msecs. 
    //     : Timer resolution is precisely 1 msec 
    // Return type  : void : 
    // Argument  : int timeout : timeout in msecs 
    /////////////////////////////////////////////////////////////// 
    void Wait(int timeout) 
    { 
     if (timeout) 
     { 
     stopCounter = timeout; 
     toLeave = true; 
     // this will do the actual delay - timer callback shares 
     // same crit section 
     EnterCriticalSection(&crit); 
     LeaveCriticalSection(&crit); 
     } 
    } 
    /////////////////////////////////////////////////////////////// 
    // Function name : TimerProc 
    // Description  : Timer callback procedure that is called 
    //     : every 1msec 
    //     : by high resolution media timers 
    // Return type  : void CALLBACK : 
    // Argument  : UINT uiID : 
    // Argument  : UINT uiMsg : 
    // Argument  : DWORD dwUser : 
    // Argument  : DWORD dw1 : 
    // Argument  : DWORD dw2 : 
    /////////////////////////////////////////////////////////////// 
    static void CALLBACK TimerProc(UINT uiID, UINT uiMsg, DWORD 
            dwUser, DWORD dw1, DWORD dw2) 
    { 
     static volatile bool entered = false; 

     PreciseTimer* pThis = (PreciseTimer*)dwUser; 
     if (pThis) 
     { 
     if (!entered && !pThis->toLeave) // block section as 
               // soon as we can 
     { 
      entered = true; 
      EnterCriticalSection(&pThis->crit); 
     } 
     else if (pThis->toLeave && pThis->stopCounter == 0) 
               // leave section 
               // when counter 
               // has expired 
     { 
      pThis->toLeave = false; 
      entered = false; 
      LeaveCriticalSection(&pThis->crit); 
     } 
     else if (pThis->stopCounter > 0) // if counter is set 
               // to anything, then 
               // continue to drop 
               // it... 
      --pThis->stopCounter; 
     } 
    } 

private: 
    MMRESULT   mRes; 
    CRITICAL_SECTION crit; 
    volatile bool toLeave; 
    volatile int  stopCounter; 
}; 

+0

你如何使用它来导致死锁? – Chad

+0

对不起,这个类中唯一的函数是Wait,并且在短暂的随机时间内调用它会导致死锁 – user3124047

+0

但是要调用Wait(),您必须有一个对象。你如何创建该对象?显示你的'main'功能。 – Chad

回答

4

EnterCriticalSection()死锁通常意味着另一个线程调用EnterCriticalSection()但从来没有所谓LeaveCriticalSection()

如图所示,此代码不是非常线程安全的(并且timeSetEvent()是一个线程计时器)。如果多个PreciseTimer定时器同时运行,它们使用相同的TimerProc()回调,因此共享相同的entered变量,但不保护它不受并发访问的影响。如果多个线程同时在相同的PreciseTimer对象上调用Wait(),则它们将跨越对方使用的stopCountertoLeave成员,这些成员也不受并发访问的保护。即使单个线程在单个PreciseTimer上调用Wait()也不安全,因为TimerProc()在其自己的线程中运行,并且stopCounter未得到充分保护。

此代码充满竞争条件。

+0

很好的答案。代码真的很乱。在'TimerProc'中的'EnterCriticalSection'调用之后,我也没有看到保证在'Wait'中执行'EnterCriticalSection'。所以等待可能根本不会发生。定时器的准确度设置也可能是一个大的电池消耗器。 –