EnterCriticalSection死锁

时间:2014-01-18 04:55:04

标签: c++ timer deadlock sleep critical-section

我找到了一些声称可以让线程在准确的时间内休眠的代码。测试代码似乎工作得很好,但是在很短的时间后它总是会死锁。

这是原始代码。我在进入和离开临界区之前放置了印刷品,并且看到它有时会连续两次离开或进入。它似乎在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;
};

1 个答案:

答案 0 :(得分:4)

EnterCriticalSection()中的死锁通常意味着另一个线程EnterCriticalSection(),但从未调用过LeaveCriticalSection()

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

此代码充满了竞争条件。