任何單個應用程序都不能徹底使該處理器達到滿負荷。當一個線程遇到較長等待時間事件時,同步多線程還容許另外一線程中的指令使用全部執行單元。例如,當一個線程發生高速緩存不命中,另外一個線程能夠繼續執行。同步多線程是 POWER5™ 和 POWER6™ 處理器的功能,可與共享處理器配合使用。緩存
SMT 對於商業事務處理負載的性能優化可達30%。在更加註重系統的總體吞吐量而非單獨線程的吞吐量時,SMT 是一個很好地選擇。安全
可是並不是全部的應用都能經過SMT 取得性能優化。那些性能受到執行單元限制的應用,或者那些耗盡全部處理器的內存帶寬的應用,其性能都不會經過在同一個處理器上執行兩個線程而獲得提升。性能優化
儘管SMT 可使系統識別到雙倍於物理CPU數量的邏輯CPU(lcpu),可是這並不意味着系統擁有了兩倍的CPU能力。服務器
SMT技術容許內核在同一時間運行兩個不一樣的進程,以此來壓縮多任務處理時所須要的總時間。這麼作有兩個好處,其一是提升處理器的計算性能,減小用戶獲得結果所需的時間;其二就是更好的能效表現,利用更短的時間來完成任務,這就意味着在剩下的時間裏節約更多的電能消耗。固然這麼作有一個總前提——保證SMT不會重複HT所犯的錯誤,而提供這個擔保的則是在酷睿微架構中表現很是出色的分支預測設計。[1]網絡
一、 Event多線程
用事件(Event)來同步線程是最具彈性的了。一個事件有兩種狀態:激發狀態和未激發狀態。也稱有信號狀態和無信號狀態。事件又分兩種類型:手動重置事件和自動重置事件。手動重置事件被設置爲激發狀態後,會喚醒全部等待的線程,並且一直保持爲激發狀態,直到程序從新把它設置爲未激發狀態。自動重置事件被設置爲激發狀態後,會喚醒「一個」等待中的線程,而後自動恢復爲未激發狀態。因此用自動重置事件來同步兩個線程比較理想。MFC中對應的類爲CEvent.。CEvent的構造函數默認建立一個自動重置的事件,並且處於未激發狀態。共有三個函數來改變事件的狀態:SetEvent,ResetEvent和PulseEvent。用事件來同步線程是一種比較理想的作法,但在實際的使用過程當中要注意的是,對自動重置事件調用SetEvent和PulseEvent有可能會引發死鎖,必須當心。架構
多線程同步-eventapp
在全部的內核對象中,事件內核對象是個最基本的。它包含一個使用計數(與全部內核對象同樣),一個BOOL值(用於指明該事件是個自動重置的事件仍是一我的工重置的事件),還有一個BOOL值(用於指明該事件處於已通知狀態仍是未通知狀態)。事件可以通知一個線程的操做已經完成。有兩種類型的事件對象。一種是人工重置事件,另外一種是自動重置事件。他們不一樣的地方在於:當人工重置的事件獲得通知時,等待該事件的全部線程均變爲可調度線程。當一個自動重置的事件獲得通知時,等待該事件的線程中只有一個線程變爲可調度線程。函數
當一個線程執行初始化操做,而後通知另外一個線程執行剩餘的操做時,事件使用得最頻繁。在這種狀況下,事件初始化爲未通知狀態,而後,當該線程完成它的初始化操做後,它就將事件設置爲已通知狀態,而一直在等待該事件的另外一個線程在事件已經被通知後,就變成可調度線程。性能
當這個進程啓動時,它建立一我的工重置的未通知狀態的事件,而且將句柄保存在一個全局變量中。這使得該進程中的其餘線程可以很是容易地訪問同一個事件對象。程序一開始建立了三個線程,這些線程在初始化後就被掛起,等待事件。這些線程要等待文件的內容讀入內存,而後每一個線程都會訪問這段文件內容。一個線程進行單詞計數,另外一個線程運行拼寫檢查,第三個線程運行語法檢查。這3個線程函數的代碼的開始部分都相同,每一個函數都調用WaitForSingleObject.,這將使線程暫停運行,直到文件的內容由主線程讀入內存爲止。一旦主線程將數據準備好,它就調用SetEvent,給事件發出通知信號。這時,系統就使全部這3個輔助線程進入可調度狀態,它們都得到了C P U時間,而且能夠訪問內存塊。這3個線程都必須以只讀方式訪問內存,不然會出現內存錯誤。這就是全部3個線程可以同時運行的惟一緣由。若是計算機上配有三個以上CPU,理論上這個3個線程可以真正地同時運行,從而能夠在很短的時間內完成大量的操做
若是你使用自動重置的事件而不是人工重置的事件,那麼應用程序的行爲特性就有很大的差異。當主線程調用S e t E v e n t以後,系統只容許一個輔助線程變成可調度狀態。一樣,也沒法保證系統將使哪一個線程變爲可調度狀態。其他兩個輔助線程將繼續等待。已經變爲可調度狀態的線程擁有對內存塊的獨佔訪問權。
讓咱們從新編寫線程的函數,使得每一個函數在返回前調用S e t E v e n t函數(就像Wi n M a i n函數所作的那樣)。
當主線程將文件內容讀入內存後,它就調用SetEvent函數,這樣操做系統就會使這三個在等待的線程中的一個成爲可調度線程。咱們不知道系統將首先選擇哪一個線程做爲可調度線程。當該線程完成操做時,它也將調用S e t E v e n t函數,使下一個被調度。這樣,三個線程會以前後順序執行,至於什麼順序,那是操做系統決定的。因此,就算每一個輔助線程均以讀/寫方式訪問內存塊,也不會產生任何問題,這些線程將再也不被要求將數據視爲只讀數據。
這個例子清楚地展現出使用人工重置事件與自動重置事件之間的差異。
P u l s e E v e n t函數使得事件變爲已通知狀態,而後當即又變爲未通知狀態,這就像在調用S e t E v e n t後又當即調用R e s e t E v e n t函數同樣。若是在人工重置的事件上調用P u l s e E v e n t函數,那麼在發出該事件時,等待該事件的任何一個線程或全部線程將變爲可調度線程。若是在自動重置事件上調用P u l s e E v e n t函數,那麼只有一個等待該事件的線程變爲可調度線程。若是在發出事件時沒有任何線程在等待該事件,那麼將不起任何做用[2]。
二、 Critical Section
使用臨界區域的第一個忠告就是不要長時間鎖住一份資源。這裏的長時間是相對的,視不一樣程序而定。對一些控制軟件來講,多是數毫秒,可是對另一些程序來講,能夠長達數分鐘。但進入臨界區後必須儘快地離開,釋放資源。若是不釋放的話,會如何?答案是不會怎樣。若是是主線程(GUI線程)要進入一個沒有被釋放的臨界區,呵呵,程序就會掛了!臨界區域的一個缺點就是:Critical Section不是一個核心對象,沒法獲知進入臨界區的線程是生是死,若是進入臨界區的線程掛了,沒有釋放臨界資源,系統沒法獲知,並且沒有辦法釋放該臨界資源。這個缺點在互斥器(Mutex)中獲得了彌補。Critical Section在MFC中的相應實現類是CcriticalSection。CcriticalSection::Lock()進入臨界區,CcriticalSection::UnLock()離開臨界區。
只能同步同一個進程的線程之間的同步,由於臨界區不能跨越進程的邊界工做。也是由於臨界區沒有name,因此不能跨進程使用。訪問臨界區以前進行鎖定,訪問後進行解鎖。若是線程B訪問線程A鎖定的臨界區,那麼線程B會被阻塞,直到線程A釋放臨界區,線程B才能夠運行。在線程B進行阻塞期間,不佔用CPU時間.
三、 Mutex
互斥量與臨界區的區別,若是一個線程鎖定了臨界區,而終止時沒有解除臨界區的鎖定,那麼等待臨界區空閒的其餘線程將無限期的阻塞下去。然而,若是鎖定互斥量的線程不能在其終止前解除互斥量的鎖定,那麼系統將認爲互斥量被「放棄」了並自動釋放該互斥量,這樣等待進程就能夠繼續執行了。
互斥器的功能和臨界區域很類似。區別是:Mutex所花費的時間比Critical Section多的多,可是Mutex是核心對象(Event、Semaphore也是),能夠跨進程使用,並且等待一個被鎖住的Mutex能夠設定TIMEOUT,不會像Critical Section那樣沒法得知臨界區域的狀況,而一直死等。MFC中的對應類爲CMutex。Win32函數有:建立互斥體CreateMutex() ,打開互斥體OpenMutex(),釋放互斥體ReleaseMutex()。Mutex的擁有權並不是屬於那個產生它的線程,而是最後那個對此Mutex進行等待操做(WaitForSingleObject等等)而且還沒有進行ReleaseMutex()操做的線程。線程擁有Mutex就好像進入Critical Section同樣,一次只能有一個線程擁有該Mutex。若是一個擁有Mutex的線程在返回以前沒有調用ReleaseMutex(),那麼這個Mutex就被捨棄了,可是當其餘線程等待(WaitForSingleObject等)這個Mutex時,仍能返回,並獲得一個WAIT_ABANDONED_0返回值。可以知道一個Mutex被捨棄是Mutex特有的。
四、 Semaphore
信號量是最具歷史的同步機制。信號量是解決producer/consumer問題的關鍵要素。對應的MFC類是Csemaphore。Win32函數CreateSemaphore()用來產生信號量。ReleaseSemaphore()用來解除鎖定。Semaphore的現值表明的意義是目前可用的資源數,若是Semaphore的現值爲1,表示還有一個鎖定動做能夠成功。若是現值爲5,就表示還有五個鎖定動做能夠成功。當調用Wait…等函數要求鎖定,若是Semaphore現值不爲0,Wait…立刻返回,資源數減1。當調用ReleaseSemaphore()資源數加1,固然不會超過初始設定的資源總數。
一、 什麼時候使用多線程?
二、 線程如何同步?
三、 線程之間如何通信?
四、 進程之間如何通信?
先來回答第一個問題,線程實際主要應用於四個主要領域,固然各個領域之間不是絕對孤立的,他們有多是重疊的,可是每一個程序應該均可以歸於某個領域:
一、 offloading time-consuming task。由輔助線程來執行耗時計算,而使GUI有更好的反應。我想這應該是咱們考慮使用線程最多的一種狀況吧。
二、 Scalability。服務器軟件最常考慮的問題,在程序中產生多個線程,每一個線程作一份小的工做,使每一個CPU都忙碌,使CPU(通常是多個)有最佳的使用率,達到負載的均衡,這比較複雜,我想之後再討論這個問題。
三、 Fair-share resource allocation。當你向一個負荷沉重的服務器發出請求,多少時間才能得到服務。一個服務器不能同時爲太多的請求服務,必須有一個請求的最大個數,並且有時候對某些請求要優先處理,這是線程優先級乾的活了。
四、 Simulations。線程用於仿真測試。
線程經常要將數據傳遞給另一個線程。Worker線程可能須要告訴別人說它的工做完成了,GUI線程則可能須要交給Worker線程一件新的工做。
經過PostThreadMessage(),能夠將消息傳遞給目標線程,固然目標線程必須有消息隊列。以消息看成通信方式,比起標準技術如使用全局變量等,有很大的好處。若是對象是同一進程中的線程,能夠發送自定義消息,傳遞數據給目標線程,若是是線程在不一樣的進程中,就涉及進程之間的通信了。下面將會講到。
進程之間的通信:
當線程分屬於不一樣進程,也就是分駐在不一樣的地址空間時,它們之間的通信須要跨越地址空間的邊界,便得采起一些與同一進程中不一樣線程間通信不一樣的方法。
一、 Windows專門定義了一個消息:WM_COPYDATA,用來在線程之間搬移數據,――無論兩個線程是否同屬於一個進程。同時接受這個消息的線程必須有一個窗口,即必須是UI線程。WM_COPYDATA必須由SendMessage()來發送,不能由PostMessage()等來發送,這是由待發送數據緩衝區的生命期決定的,出於安全的須要。
二、 WM_COPYDATA效率上面不是過高,若是要求高效率,能夠考慮使用共享內存(Shared Memory)。使用共享內存要作的是:設定一塊內存共享區域;使用共享內存;同步處理共享內存。
第一步:設定一塊內存共享區域。首先,CreateFileMapping()產生一個file-mapping核心對象,並指定共享區域的大小。MapViewOfFile()得到一個指針指向可用的內存。若是是C/S模式,由Server端來產生file-mapping,那麼Client端使用OpenFileMapping(),而後調用MapViewOfFile()。
第二步:使用共享內存。共享內存指針的使用是一件比較麻煩的事,咱們須要藉助_based屬性,容許指針被定義爲從某一點開始起算的32位偏移值。
第三步:清理。UnmapViewOfFile()交出由MapViewOfFile()得到的指針,CloseHandle()交出file-mapping核心對象的handle。
第四步:同步處理。能夠藉助Mutex來進行同步處理。
三、 IPC
1)Anonymous Pipes。Anonymous Pipes只被使用於點對點通信。當一個進程產生另外一個進程時,這是最有用的一種通信方式。
2)Named Pipes。Named Pipes能夠是單向,也能夠是雙向,而且能夠跨越網絡,步侷限於單機。
3)Mailslots。Mailslots爲廣播式通信。Server進程能夠產生Mailslots,任何Client進程能夠寫數據進去,可是隻有Server進程能夠取數據。
4)OLE Automation。OLE Automation和UDP都是更高階的機制,容許通信發生於不一樣進程間,甚至不一樣機器間。
5)DDE。DDE動態數據交換,使用於16位Windows,目前這一方式應儘可能避免使用。[3]
內容來自網絡資源:http://baike.baidu.com/view/2808915.htm