MFC多執行緒編的可能

來源:酷知科普網 2.99W

之所以是“可能”,因為這裡有個重點就是臨時物件是HWND操作的封裝,不是視窗類的封裝。因此所有的HWND臨時物件都是CWnd的例項,即使上面強行轉換為CAbcDialog*也依舊是CWnd*,所以在ASSERT_VALID裡呼叫CAbcDialog::AssertValid時,其定義了一些附加檢查,則可能發現這是一個CWnd的例項而非一個CAbcDialog例項,導致斷言失敗。因此應將CAbcDialog全部換成CWnd,這下雖然不斷言失敗了,但依舊錯誤(先不提pDialog->m_Data怎麼辦),因為臨時物件是HWND操作的封裝,而不幸的是UpdateData只是MFC自己提供的一個對話方塊資料交換的機制(DDX)的操作,其不是通過向HWND傳送訊息來實現的,而是通過虛擬函式機制。因此在UpdateData中呼叫例項的DoDataExchange將不能呼叫CAbcDialog::DoDataExchange,而是呼叫CWnd::DoDataExchange,因此將不發生任何事。

步驟/方法

(01)因此合理(並不一定最好)的解決方法是向CAbcDialog的例項傳送一個訊息,而通過一箇中間變數(如一全域性變數)來傳遞資料,而不是使用CAbcDialog::m_Data。當然,如果資料少,比如本例,就應該將資料作為訊息引數進行傳遞,減少程式碼的複雜性;資料多則應該通過全域性變數傳遞,減少了緩衝的管理費用。修改後如下:#define AM_DATANOTIFY ( WM_USER + 1 )static DWORD g_Data = 0;DWORD WINAPI ThreadProc( void *pData ) // 執行緒函式(比如用於從COM口獲取資料)BEGIN_MESSAGE_MAP( CAbcDialog, CDialog )…ON_MESSAGE( AM_DATANOTIFY, OnDataNotify )…END_MESSAGE_MAP()BOOL CAbcDialog::OnInitDialog(){CDialog::OnInitDialog();// 其他初始化程式碼CreateThread( NULL, 0, ThreadProc, m_hWnd, 0, NULL ); // 建立執行緒return TRUE;}LRESULT CAbcDialog::OnDataNotify( WPARAM /* wParam */, LPARAM /* lParam */ ){UpdateData( FALSE );return 0;}void CAbcDialog::DoDataExchange( CDataExchange *pDX ){CDialog::DoDataExchange( pDX );DDX_Text( pDX, IDC_EDIT1, g_Data );}

MFC多執行緒編的可能

(02)注意事項“執行緒安全”是一個什麼概念?以前常聽高手告誡MFC物件不要跨執行緒使用,因為MFC不是執行緒安全的。比如CWnd物件不要跨執行緒使用,可以用視窗控制代碼(HWND)代替。CSocket/CAsyncSocket物件不要跨執行緒使用,用SOCKET控制代碼代替.那麼到底什麼是執行緒安全呢?什麼時候需要考慮?如果程式涉及到多執行緒的話,就應該考慮執行緒安全問題。比如說設計的介面,將來需要在多執行緒環境中使用,或者需要跨執行緒使用某個物件時,這個就必須考慮了。關於執行緒安全也沒什麼權威定義。在這裡我只說說我的理解:所提供的介面對於執行緒來說是原子操作或者多個執行緒之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。一般而言“執行緒安全”由多執行緒對共享資源的訪問引起。如果呼叫某個介面時需要我們自己採取同步措施來保護該介面訪問的共享資源,則這樣的介面不是執行緒安全的和STL都不是執行緒安全的. 怎樣才能設計出執行緒安全的類或者介面呢?如果介面中訪問的資料都屬於私有資料,那麼這樣的介面是執行緒安全的.或者幾個介面對共享資料都是隻讀操作,那麼這樣的介面也是執行緒安全的.如果多個介面之間有共享資料,而且有讀有寫的話,如果設計者自己採取了同步措施,呼叫者不需要考慮資料同步問題,則這樣的介面是執行緒安全的,否則不是執行緒安全的。

MFC多執行緒編的可能 第2張

(03)多執行緒的程式設計應該注意些什麼呢1、儘量少的使用全域性變數、static變數做共享資料,儘量使用引數傳遞物件。被引數傳遞的物件,應該只包括必需的成員變數。所謂必需的成員變數,就是必定會被多執行緒操作的。很多人圖省事,會把this指標(可能是任意一個物件指標)當作執行緒引數傳遞,致使執行緒內部有過多的操作許可權,對this中的引數任意妄為。整個程式由一個人完成,可能會非常注意,不會出錯,但只要一轉手,程式就會面目全非。當兩個執行緒同時操作一個成員變數的時候,程式就開始崩潰了,更糟的是,這種錯誤很難被重現。(我就在鬱悶這個問題,我們是幾個人,把程式編成debug版,經過數天使用,才找到錯誤。而找到錯誤只是開始,因為你要證明這個bug被修改成功了,也非常困難。)其實,執行緒間資料互動大多是單向的,在執行緒回撥函式入口處,儘可能的將傳入的資料備份到區域性變數中(當然,用於執行緒間通訊的變數不能這麼處理),以後只對區域性變數做處理,可以很好的解決這種問題。2、在MFC中請慎用執行緒。因為MFC的框架假定你的訊息處理都是在主執行緒中完成的。首先視窗控制代碼是屬於執行緒的,如果擁有視窗控制代碼的執行緒退出了,如果另一個執行緒處理這個視窗控制代碼,系統就會出現問題。而MFC為了避免這種情況的發生,使你在子執行緒中呼叫訊息(視窗)處理函式時,就會不停的出Assert錯誤,煩都煩死你。典型的例子就時CSocket,因為CSocket是使用了一個隱藏視窗實現了假阻塞,所以不可避免的使用了訊息處理函式,如果你在子執行緒中使用CSocket,你就可能看到assert的彈出了。3、不要在不同的執行緒中同時註冊COM元件。兩個執行緒,一個註冊, , , ; 而另一個則註冊, , , ,結果死鎖發生了,分別死在FreeLibrary和DllRegisterServer,因為這8個ocx是用MFC中做的,也可能是MFC的Bug,但DllRegisterServer卻死在GetModuleFileName裡,而GetModuleFileName則是個API唉!如果有過客看到,恰巧又知道其原因,請不吝賜教。4、不要把執行緒搞的那麼複雜。很多初學者,恨不能用上執行緒相關的所有的函式,這裡互斥,那裡等待,一會兒起執行緒,一會兒關執行緒的,比起goto語句有過之而無不及。好的多執行緒程式,應該是儘量少的使用執行緒。這句話怎麼理解吶,就是說盡量統一一塊資料共享區存放資料佇列,工作子執行緒從佇列中取資料,處理,再放回資料,這樣才會模組化,物件化;而不是每個資料都起一個工作子執行緒處理,處理完了就關閉,寫的時候雖然直接,等維護起來就累了。

MFC多執行緒編的可能 第3張
熱門標籤