我找到了一些声称可以让线程在准确的时间内休眠的代码。测试代码似乎工作得很好,但是在很短的时间后它总是会死锁。
这是原始代码。我在进入和离开临界区之前放置了印刷品,并且看到它有时会连续两次离开或进入。它似乎在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 :(得分:4)
EnterCriticalSection()
中的死锁通常意味着另一个线程EnterCriticalSection()
,但从未调用过LeaveCriticalSection()
。
如图所示,此代码不是非常线程安全的(timeSetEvent()
是一个线程计时器)。如果多个PreciseTimer
计时器同时运行,则它们使用相同的TimerProc()
回调,因此共享相同的entered
变量,而不保护它不受并发访问的影响。如果多个线程同时在同一个Wait()
对象上调用PreciseTimer
,他们将会逐步使用stopCounter
和toLeave
成员,也不保护他们免受并发访问。即使在单个Wait()
上调用PreciseTimer
的单个线程也不安全,因为TimerProc()
在其自己的线程中运行且stopCounter
没有得到充分保护。
此代码充满了竞争条件。