線程(vc)

       我主要是做VC下的c++工作,因此對VC比較熟悉的人會對本篇文章有更多的共同的感觸吧。

        我剛畢業的時候,非常害怕,害怕一個鼠標點擊函數執行半截被中斷或者裏面的代碼中的變量被其他莫名奇妙的東西給篡改。實際上mfc會話框程序,只有一個主線程,可以不用考慮這些事情的,有些杞人憂天。

       這篇文章是09年寫的,後來多年後的認識也加了進去。比如SOCKET消息和OnTimer處理的認識,我不知道到底這些消息的處理是不是mfc在後面起了一個單獨的線程來激發,要是如此OnTimer裏面的函數處理就要小心要加互斥了,後來在蘇哥的指點下,進行驗證。在OnTimer函數中加Sleep(60*1000);如果界面假死沒有反應說明OnTimer是在主線程中被調用的。同理OnReceive也可驗證。當然你調用recv,基本上是啓動接收線程了,別論。

        簡明扼要的說就是隻要你不在mfc中啓動新線程,你就不需要擔心,一段代碼執行半截被掛起的情況,也就不用考慮互斥臨界區的問題了。但是往往我們需要加入線程,爲了處理事務,不讓界面卡頓或者爲了更快的處理事務,比如迅雷下載,就起了很多個下載線程。

       爲什麼我們要啓動線程?啓動多個線程後,就代表着一段代碼,就可能被多個線程訪問,造成此代碼中的變量不可控(成員變量不可控,局部變量,因爲每次函數執行都有局部變量環境的保存,因此局部變量不用考慮),這種併發該如何在程序中解決。

     1 爲什麼我們要啓動線程,剛把原因做了簡單的介紹。最主要的原因我們知道CPU的速度非常快,電腦的上的其他操作比較慢,比如硬盤上文件的讀取就比較慢,我們爲了充分利用CPU,才啓動多個線程爲我們的應用服務。舉個例子,比如你正在從電腦上往U盤上拷貝電影,你想想往U盤寫的過程,cpu執行指令寫,然後磁盤操作,磁盤寫的時候,CPU還有90%的空閒;這個時候你可以利用CPU空閒的時間,從內存中已經加載的電影內容,進行預覽。拷文件和預覽觀看電影同時進行。再舉個例子,我看能不能進一步把你說迷糊了。就是搬運工往傳送帶上放東西,那傳送帶就是CPU,能力強,多個搬運工來往往傳送帶上放東西,傳送帶都能處理的過來;可以理解搬運工就是多個子線程。哎,算了不舉例子了,我自己都迷糊了。

     2 爲什麼要考慮併發問題。因爲雖然從宏觀上講充分利用cpu資源,可以併發任務,但是CPU執行指令是一條一條執行的。當啓動多個線程時,就會發生指令中斷,指令中斷造成的結果,就是一個線程的執行被掛起;執行另外的線程,另外的線程就可能訪問前者訪問的東西,造成髒數據。

     3 下面我們還是講怎麼解決併發的問題了,因爲涉及到一段代碼會被多個線程同時訪問,我們就需要在這段代碼上下加上臨界區,進的時候加鎖,出的時候解鎖。進的時候先判斷鎖時候在解着,是才能執行下一句代碼,這樣保證這段代碼數據是單步執行,不會出問題的。當然沒必要在整段代碼加,可以在訪問成員變量,或者全局變量時加上。項目上一般多數都有個內存管理鏈表,來管理互斥數據,在鏈表上的添加、取數據時加上互斥。比如在線看視頻,簡單的是接收網絡數據一個線程,對數據進行解碼一個線程,這樣兩個線程同時工作,觀看更流程;如果你把視頻解碼也放在一個線程裏面,你想想,一個視頻解碼耗費時間多,等解碼完了才能去接收下一步的數據,你想想在解碼的時候,接收工作本可以充分空閒CPU的,時間被白白浪費了。當然了接收起更多的線程,更好,你就不用等了好一會,才能看到那雞動的視頻了。

     希望你能夠平心的無聊的看到這裏,下面我畫個簡單的圖,實際上mfc的recv去取數據的時候,可能數據已經被網卡放在SOCKET系統接收緩存中了吧。


我也不知道理解對不對,反正我是想說明白,哎。

 下面是本人對線程利用的一些總結。你看到這裏,說明你已經只要兩年的c++經驗了。

1 慎用線程,儘量不要用,若用,程序的整個邏輯會一塌糊塗,你補救都補救不過來。

2 如果整個程序的某一塊要提高效率,與其他模塊牽扯不多,最好了,就把線程封裝到這個模塊中。

3 假若這個模塊是核心,又確實要用到線程,你就需要把所有相關的業務統一管理,以消息的形式發送這個線程的消息隊列裏,統一管理,逐條消息進行處理,就不會有同時訪問造成的數據Delete錯誤了。

拿我2010年做的一個項目吧來說,我們當時做呼叫中心的二次開發。板卡的開發比較簡單,廠商用的是OnTimer遍歷機制,大家知道OnTimer是在主進程的主線程中執行的,因此不會有異步的問題。但是一體機就麻煩了,用的是回調,經過驗證回調實在廠商的底層的一個線程中執行,悲劇了,回調所激發的底層設備消息,然後上層業務的作息的呼叫事件等,是在主線程中直接調用底層的函數,兩個線程同時可能調用一個函數了或者對象,當時我們也想到了消息隊列,就是剛的思想統一管理設備消息。但是兩個問題,造成軟件還是不穩定,第一個就是當時用的是mfc的postMessage,postMessage是基於窗體的,因此當界面有卡頓的時候就會有消息丟失現象。第二個問題就是當時統一所有消息不徹底,有的函數執行並沒有統一到那個主線程裏(這裏的主線程,是我們主要用來處理業務的線程,並非mfc的主進程的主線程),應該叫做業務主線程。思路上應該是底層的設備消息(底層線程激發),上層作息操作消息(主進程裏的主線程激發)都要統一發送給主業務線程,由主業務線程統一管理,循環依次取消息進行處理。

當然了,網上有很多別人封裝好的消息管理鏈表,線程對象等,你都可以拿過來用。

當然具體的業務具體的對待,是個艱鉅的活,希望我們能夠有收穫,理順我們的代碼,刪除多餘代碼,多測試,以備後面可以重複利用。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章