2014-01-18 4 views
1

Я нашел код, который утверждал, что можно сделать поток поспать на точное количество времени. Проверяя код, он работает отлично, однако он всегда блокируется через короткий промежуток времени.EnterCriticalSection Deadlocking

Оригинальный код. Я помещал отпечатки перед входом и выходом из критической секции и видел, что иногда он уходит или входит дважды подряд. Кажется, что он зашел в тупик при вызове EnterCriticalSection в функции Wait.

Есть ли способ, которым я могу изменить этот код, чтобы сохранить его функциональность, а не затормозить?

//---------------------------------------------------------------- 
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, не защищая ее от одновременного доступа. И если несколько потоков вызовут Wait() на одном и том же объекте PreciseTimer в то же время, они собираются перешагнуть друг друга с помощью членов stopCounter и toLeave, которые также не защищены от параллельного доступа. Даже одна нить, вызывающая Wait() на одном PreciseTimer, небезопасна, так как TimerProc() работает в своей собственной нити, а stopCounter не защищен должным образом.

Этот код полон условий гонки.

+0

Хороший ответ. Код действительно беспорядок. Я также не вижу гарантии, чтобы 'EnterCriticalSection' в' Wait' выполнялся после вызова 'EnterCriticalSection' в' TimerProc'. Таким образом, ожидание может не произойти вообще. Установка точности таймера также может быть большим разрядником батареи. –