MFC多線程編的可能
之所以是“可能”,因爲這裏有個重點就是臨時對象是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 );}
(02)注意事項“線程安全”是一個什麼概念?以前常聽高手告誡MFC對象不要跨線程使用,因爲MFC不是線程安全的。比如CWnd對象不要跨線程使用,可以用視窗句柄(HWND)代替。CSocket/CAsyncSocket對象不要跨線程使用,用SOCKET句柄代替.那麼到底什麼是線程安全呢?什麼時候需要考慮?如果程序涉及到多線程的話,就應該考慮線程安全問題。比如說設計的接口,將來需要在多線程環境中使用,或者需要跨線程使用某個對象時,這個就必須考慮了。關於線程安全也沒什麼權威定義。在這裏我只說說我的理解:所提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。一般而言“線程安全”由多線程對共享資源的訪問引起。如果調用某個接口時需要我們自己採取同步措施來保護該接口訪問的共享資源,則這樣的接口不是線程安全的和STL都不是線程安全的. 怎樣才能設計出線程安全的類或者接口呢?如果接口中訪問的數據都屬於私有數據,那麼這樣的接口是線程安全的.或者幾個接口對共享數據都是隻讀操作,那麼這樣的接口也是線程安全的.如果多個接口之間有共享數據,而且有讀有寫的話,如果設計者自己採取了同步措施,調用者不需要考慮數據同步問題,則這樣的接口是線程安全的,否則不是線程安全的。
(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語句有過之而無不及。好的多線程程序,應該是儘量少的使用線程。這句話怎麼理解吶,就是說盡量統一一塊數據共享區存放數據隊列,工作子線程從隊列中取數據,處理,再放回數據,這樣纔會模組化,對象化;而不是每個數據都起一個工作子線程處理,處理完了就關閉,寫的時候雖然直接,等維護起來就累了。
-
不用U盤,安裝純淨版Windows系統
安裝系統的方法有好多種,U盤製作啓動盤安裝時,會自帶一些軟件瀏覽器啊、小遊戲、桌面壁紙什麼,萬能驅動的,裝完後又要一個個卸載,但是註冊列表又沒法清理乾淨。如何安裝純淨版的Windows系統,讓我們一起跟着步驟來實現吧!操作方法(01)在搜狗瀏覽器中搜尋:MSDN,選擇官方網站...
-
QQ瀏覽器如何安裝應用插件
QQ瀏覽器如何安裝應用插件,瀏覽器的應用插件的安裝對我們來說是非常方便的。下面以安裝護眼導航應用爲例,介紹如何安裝瀏覽器應用插件。操作方法(01)開啟QQ瀏覽器,點擊右上角的“”+“”如圖所示(02)進入了安裝應用的首頁,如圖所示(03)比如安裝一個護眼的應用,鼠標停留在...
-
電腦顯示屏幕不亮 筆記本不能開機啓動
電腦非正常關機或者用久了,再次開啟,突然打不開了,屏幕顯示不亮,連繫統都進不了怎麼回事?何種情況下,需要維修?操作方法(01)第一檢視信號指示燈。先是判斷實際能否開機,若開機了指示燈會有閃爍。黑屏有時並非全黑屏,仔細看是灰屏,此時信號燈開機時有閃爍。說明硬件沒有問題...
-
Excel YEARFRAC函數的使用方法
YEAR函數用來抽取指定日期中年份的函數,在Excel中有一個比較類似的YEARFRAC函數。這個函數在我們日常的學習生活中作用非常大,那麼這個函數怎麼使用?它又能給我們帶來多大的便利呢?請和我一起學習這個函數。操作方法(01)YEARFRAC函數的功能計算指定的開始時間和指定...