原文轉載自http://www.cnblogs.com/xingrun/p/3587144.html?utm_source=tuicool&utm_medium=referral。
以下全為轉載:
寫在前面:
晚上應該繼續完成未寫完的代碼,但Chrome上打開的标簽實在太多了,約30個了,必須關掉一些,是以需要把自己看的整理一下然後關掉。本次主要寫點MFC環境下多線程式列槽通信相關的東西,這包括線程建立及控制、序列槽同步異步操作、記憶體非法通路(或者說是線程同步)、線程通信、Windows消息響應過程等。
遇到問題:
項目中IO傳感器通信子產品之前直接寫在了主線程中,UI代碼和序列槽通信代碼攪合在一起,不利于後期維護,而且有個非常嚴重的問題,IO通信太忙導緻整個系統比較卡,特别是當系統接上超過3個錄影機之後,MFC模态對話框使用Domodal()直接無法打開,卡住了,然後使用者就無法操作了,這個問題必須要解決。
解決方案:
單獨開辟一個線程來處理所有的序列槽通信,該IO線程和主線程(負責UI部分)通信,進而更新狀态,不能子線程中直接更新UI,參看《MFC最好不要在子線程中操控界面上的控件》。
具體步驟:
1.創立IO線程并完成消息響應
1 2 | |
其中
1 2 | |
IOControlProc線程函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | |
這裡,IO線程有自己的消息循環隊列(雖然沒有視窗),參看:《子線程裡如何使用定時器》,把這裡的代碼改成死循環的(參看《是否在子線程内使用SetTimer?》),如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
我的消息響應函數寫成如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
這裡要注意一個問題,OnIOTimer中獲得的idEvent并不是我們設定的IOCOMM_TIMER或者TEMPHUMICOMM_TIMER,究其原因,參看《SetTimer在無視窗和有視窗線程的使用》,文中關于原因的解釋為“注:隻有當hWnd參數為非空時,計時器的ID為設定的 nIDEvent, 系統為你自動生成一個計時器ID,可由傳回時值擷取.”,可由MSDN得知。
那麼我們的這個IOCOMM_TIMER或者TEMPHUMICOMM_TIMER,怎麼傳遞過去呢?檢視《怎麼往SetTimer的回調函數傳遞參數》得知,我們可由msg.wParam傳遞。
那麼對于我們自定義的消息TEMPHUMICOMM_TIMER + IOCOMM_TIMER,我也想讓OnIOTimer來處理怎麼辦?直接修改msg.message = WM_TIMER;就可以了嘛?答案是不行的!
為什麼呢?參看《消息循環中的TranslateMessage函數和DispatchMessage函數》,原來“如果參數lpmsg指向一個WM_TIMER消息,并且WM_TIMER消息的參數IParam不為NULL,則調用IParam指向的函數,而不是調用視窗程式。”,那麼我們直接修改msg.lParam為OnIOTimer函數的位址就行了。這樣,所有消息響應完成了。
2.多線程通路沖突(線程沖突)
遇到一個問題,系統有一個序列槽通信端口,IO線程直接使用了,然後我在主線程中也發送資料,然後問題出現了,有時候會出現非法通路,跟蹤了一下,原來兩個線程使用了同一個序列槽通信緩沖區,主線程往裡面壓入資料的時候,可能子線程已經釋放了該緩沖區,查詢文章《CSerialPort連續發送大量資料時出錯原因分析》,而我使用的CSerialPort是同步的,我嘗試修改類庫代碼,報錯太多,此方法放棄。最終,我的解決方法是把所有的序列槽IO通信全部交給子線程來做,那麼主線程要做的事,可以通過發送消息給子線程,再由子線程代勞,主線程怎麼發送消息給子線程?使用API函數PostThreadMessage來完成,第一個參數就是子線程的Id。
子線程收到資料後,進行驗證,驗證通過後,發消息給主線程,通知它更新界面。《主線程與子線程間通信解決辦法 - VC/MFC》
3.主線程退出前,關閉子線程
1 2 | |
GetMessage有消息時且消息不為WM_QUIT時傳回TRUE,如果有消息且為WM_QUIT則傳回FALSE,沒有消息時不傳回。
這裡可參看《如何正确的關閉 MFC 線程》和《GetMessage和PeekMessage的聯系與差別以及用法 TranslateMessage與DispatchMessage 》
至此,經測試問題全部解決,記錄一下。