我遭遇到了調用waveOutReset死鎖的問題,在GOOGLE上一搜,遇到同樣問題的人還真不少,但沒有人很明確地找到造成DEADLOCK的原因,都是糊里糊塗就把問題解決了,然後把運行OK的代碼一貼完事。我花了四五個小時才徹底摸清楚規律,把這經驗拿出來共享
原則:
(1) waveOutReset不是立即返回的函數, 而需要等待駐留在WAVEDEV裏的音頻BUFFER全部標記爲WOM_DONE,經過callback proc處理完畢後, waveOutReset纔會返回.
(2) 在waveOutReset調用到返回之間的這段時間內, 禁止任何線程對處於reset中的HWAVEOUT調用任何WAVE API, 否則立刻造成死鎖.
把握住這兩個原則就可以根治waveOutReset死鎖問題了. 不過爲了把事情描述得更具體些, 我把網上多數人造成死鎖的情況列一下, 並給出解決方法
第一種死法:
在回調處理函數WaveCallbackFunc中,對標記爲WOM_DONE的BUFFER進行waveOutUnprepareHeader
錯誤分析:
調用waveOutReset後, 系統內所有未播放的buffer全部被標記爲WOM_DONE返回給WaveCallbackFunc, 這屬於waveOurReset處理過程的一部分, 此時waveOurReset還未返回. 所以這時如果調用到waveOutUnprepareHeader, 就會立刻造成死鎖. 我自己也是犯的這個錯誤。
解決方法:
在調用waveOurReset之前設置一個FLAG, 然後在WaveCallbackFunc裏要調用waveOutUnprepareHeader之前判斷一下這個FLAG, 如果處於reset過程中就跳過waveOutUnprepareHeader. 範例代碼:
static CRITICAL_SECTION g_CS;
static BOOL g_bResetting = FALSE;
void Reset()
{
EnterCriticalSection(&g_CS); //之前InitializeCriticalSection一下
g_bResetting = TRUE;
LeaveCriticalSection(&g_CS);
waveOutReset();
g_bResetting = FALSE; //RESET後就不會調WaveCallbackFunc了,所以不需要鎖
}
VOID CALLBACK WaveCallbackFunc(HWAVEOUT hwo, UINT32 uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
EnterCriticalSection(&g_CS); //防止g_bResetting在if判斷之後, waveOutUnprepareHeader之前被設成1了, callback和reset處於不同線程
if( (uMsg == WOM_DONE) && !g_bResetting )
{
waveOutUnprepareHeader(hWaveOut, pWaveHdr, sizeof(WAVEHDR));
}
LeaveCriticalSection(&g_CS);
}
另外, 如果使用了每個BUFFER定長的BUFFER-CHAIN來做生產者-消費者模型, 那麼在BUFFER-CHAIN初始化的時候順便把所有WAVEHDR都PREPARE好, 而只在BUFFER-CHAIN全部釋放的時候才做UNPREPARE, 那麼在每次waveOutWrite和WOM_DONE的循環裏就不需要PREPARE/UNPREPARE了,這也能避開上述情況造成的死鎖
第二種死法: 某些人喜歡在在回調處理函數WaveCallbackFunc中,調用waveOutWrite往系統裏填充新BUFFER.
錯誤分析: 同情況一, 在waveOutReset過程中,如果WaveCallbackFunc調用了waveOutWrite, 同樣會造成死鎖.
解決方法: 同情況一, 設置FLAG和臨界區配合來防止. 不過比較建議在另外的線程中調waveOutWrite寫入BUFFER, 這才符合生產-消費模型.
第三種死法: 在另外一個線程中調用waveOurWrite, 但是waveOutReset之前, 沒有停下那個線程
錯誤分析: RESET過程中,還有waveOurWrite調用
解決方法: 把waveOurWrite那個線程suspend或退出, 或者在waveOutWrite前面用FLAG+臨界區判斷
第四種死法: 也是最匪夷所思的死法, 從UI的某個按鈕一路調用到waveOutReset(處於同線程中), 而在WaveCallbackFunc中,同線程調用SetWindowText之類UI函數
錯誤分析: 事實是:
(1) 如果是在WindowProc的某個CASE裏調用SetWindowText, 調用者和SetWindowText處於同線程中,那麼運行正常, 多數UI也都會這麼寫;
(2) 如果處於Thread1的UI調用了waveOutReset, 導致處於Thread2中的WaveCallbackFunc運行並調用SetWindowText, 試圖設置處於Thread1的UIcallbackProc, 那麼SetWindowText會死鎖, 導致WaveCallbackFunc死掉, 最後表面上看起來就是waveOutReset死掉.其實我也不明白爲什麼這樣也能死,要問就去問蓋茨大叔吧.
解決方法:
在WaveCallbackFunc中,用RESET FLAG + 臨界區的方法保護UI調用函數, 使其在waveOutReset過程中不被調用, 或者用PostMessage這樣的異步函數,實在不得不同步影響UI的話就忍一忍等waveOurReset函數返回後再做吧.
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/renjwjx/archive/2009/03/03/3954012.aspx