主執行緒,子執行緒資源衝突,解決方案
問題描述, 我開發的任性動圖軟體,曾經遇到過這樣的問題:
任性動圖有一個功能是塗鴉功能,就是將你的塗鴉過程生成動圖,怎麼實現的呢?
有一個主顯示記憶體,用以顯示介面影象
有一個輔顯示記憶體,用以生成動圖時,繪製動圖
因為要繪製塗鴉,所以
開闢了一個塗鴉記憶體,將塗鴉繪製在這個記憶體上,然後再分別拷貝到主次記憶體上。
因為要生成動圖,而且生成的過程中,要適時塗鴉,所以啟用執行緒的方式。
開闢了一個執行緒,這個執行緒負責將塗鴉過程,生成GIF動圖
主執行緒複雜繪製塗鴉,然後將繪製好的塗鴉記憶體,拷貝到顯示記憶體上。
問題來了:生成的GIF動圖,發現很多時候,某一幀是空白的,也就是說,那一幀 並沒有將塗鴉繪製到動圖上。
原因:
後來經分析,原因很可能是這兒產生了衝突 。
主執行緒繪製塗鴉,
主執行緒將塗鴉記憶體拷貝到主記憶體上
子執行緒將塗鴉記憶體拷貝到輔記憶體上
這時就有可能產生衝突了: 主執行緒在繪製塗鴉記憶體的時候,正好遇到子執行緒想將塗鴉記憶體拷貝到輔記憶體上,也就是主執行緒和子執行緒同時操作塗鴉記憶體,這樣就產生矛盾了,由此引發了這個問題。
解決:
1 初始化訊號
HANDLE m_hDrawScrawl ; //自動復原 有訊號 保證生成動圖過程中 繪製的完整性
m_hDrawScrawl = CreateEvent(NULL, TRUE, TRUE, NULL); //
2
主執行緒中,塗鴉記憶體處理過程,設定為無訊號
繪製塗鴉函式中,繪製過程中,設定為無訊號,繪製結束後,再置位有訊號。
DrawScrawl(UINT nFlags, CPoint nPoint){
//塗鴉
ResetEvent(m_hDrawScrawl);//設定為無訊號
Draw()//繪製塗鴉過程。。。。。
//。。。。。
SetEvent(m_hDrawScrawl);
}
主執行緒塗鴉拷貝過程設定為無訊號
ResetEvent(m_hDrawScrawl);//設定為無訊號 dcMem.TransparentBlt(0, 0, m_rcbmMem.Width(), m_rcbmMem.Height(), &dcTuyaMem, 0, 0, m_rcbmMem.Width(), m_rcbmMem.Height(), RGB(255, 255, 255)); if (m_bTuya&&m_nShapeStyle == 1){ dcMem.TransparentBlt(0, 0, m_rcbmMem.Width(), m_rcbmMem.Height(), &dcTuyaMem_Sec, 0, 0, m_rcbmMem.Width(), m_rcbmMem.Height(), RGB(255, 255, 255)); } SetEvent(m_hDrawScrawl);//設定為有訊號
3 子執行緒等待主執行緒的塗鴉記憶體操作完畢後,再操作
WaitForSingleObject(m_hDrawScrawl, 4000);
dcMem.TransparentBlt(pMiddleChunk->Left, pMiddleChunk->Top, pMiddleChunk->Width, pMiddleChunk->Height, &dcTuyaMem, pMiddleChunk->Left, pMiddleChunk->Top, pMiddleChunk->Width, pMiddleChunk->Height, RGB(255, 255, 255));
當然,這隻考慮了一層,只考慮了子執行緒處理時,一定要等主執行緒完畢後才可,沒有考慮子執行緒處理塗鴉記憶體時,防止主執行緒也在處理。因為我的子執行緒只是個拷貝,所以沒有雙向考慮。
要想主執行緒、輔執行緒都不打擾。
可能要試試下面的策略
設定同一個訊號量,用這個訊號量進行控制,主執行緒等待子執行緒完畢後,再進行,子執行緒等待主執行緒完畢後,再進行。
hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
主執行緒:
WaitForSingleObject(hEvent,INFINITE)
ResetEvent(hEvent)
.....
SetEvent(hEvent)
輔執行緒:
WaitForSingleObject(hEvent,INFINITE)
ResetEvent(hEvent)
.....
SetEvent(hEvent)
比如:
子執行緒繪圖函式 DrawGifOnDC(memDC, i);
主執行緒繪圖函式 DrawXml();
兩者有衝突,可以這麼解決
子執行緒:
WaitForSingleObject(m_hDrawxml, INFINITE);
ResetEvent(m_hDrawxml);
DrawGifOnDC(memDC, i);
SetEvent(m_hDrawxml);
主執行緒
WaitForSingleObject(m_hDrawxml, INFINITE);
ResetEvent(m_hDrawxml);
DrawXml();
SetEvent(m_hDrawxml);