l 由于線程是同享進(jìn)程內(nèi)存的,所以通過全局/靜態(tài)變量來進(jìn)行通訊效力最最高的。參數(shù)需要斟酌是不是加volitile。
l 通過傳遞的參數(shù),如援用和指針。參數(shù)需要斟酌是不是加volitile。
SendMessage必須等待消息函數(shù)處理完成才返回,PostMessage則直接將消息放入消息隊列立即返回。所以SendMessage的消息參數(shù)可以是臨時變量,而PostMessage的消息參數(shù)必須保證足夠的生存周期。
即多個線程彼此獨立,不受外部線程的影響。線程本身就是實現(xiàn)異步的1種方式。
即多個線程彼此依賴,線程A的計算結(jié)果是線程B的計算的條件,也就是說在開始線程B的計算之前必須等待線程A的計算完。
即多個線程在操作同1個資源時,1個線程必須等另外一個線程結(jié)束了才能繼續(xù)操作。互斥與同步不同的地方是,互斥沒有前后關(guān)系。同1個資源,可以指全局變量,也能夠指1個文件對象或是其他的內(nèi)核對象。由于內(nèi)核對象是跨進(jìn)程的,所以更是跨線程的。
WaitForSingleObject函數(shù)是等待內(nèi)核對象從無信號狀態(tài)到有信號狀態(tài)或是超時即返回。也即無信號狀態(tài)時等待,有信號或超時立即返回。
WaitForMulitpleObjects函數(shù)是等待多個內(nèi)核對象從無信號狀態(tài)到有信號狀態(tài)或是超時即返回(可以指明是所有對象或是任1對象)。
Windows具有幾種內(nèi)核對象可以處于已通知狀態(tài)和未通知狀態(tài):進(jìn)程、線程、作業(yè)、文件、控制臺輸入/輸出/毛病流、事件、等待定時器、信號量、互斥對象。
對象 |
無信號狀態(tài) |
有信號狀態(tài) |
成功等待副作用 |
進(jìn)程 |
進(jìn)程活動時 |
進(jìn)程終止時 |
無 |
線程 |
線程活動時 |
線程終止時 |
無 |
文件 |
I/O要求正在處理時 |
I/O要求結(jié)束時 |
無 |
控制臺輸入 |
不存在任何輸入 |
存在輸入時 |
無 |
文件修改通知 |
沒有任何文件修改通知 |
文件系統(tǒng)發(fā)現(xiàn)修改時 |
重置通知 |
自動重置事件 |
ResetEvent, PulseEvent或等待成功 |
當(dāng)調(diào)用SetEvent或PulseEvnet時 |
重置事件 |
人工重置事件 |
ResetEvent,或PulseEvent |
當(dāng)調(diào)用SetEvent或PulseEvnet時 |
無 |
自動重置定時器 |
CancelWaitableTimer或等待成功 |
當(dāng)時間到時(SetWaitableTimer) |
重置定時器 |
人工重置定時器 |
CancelWaitableTimer |
當(dāng)時間到時(SetWaitableTimer) |
無 |
信號量 |
等待成功 |
當(dāng)資源數(shù)量>0時(ReleaseSemaphore) |
數(shù)量減1 |
互斥量 |
等待成功 |
當(dāng)未被線程具有時(ReleaseMutex) |
獲得線程所有權(quán) |
l 線程和進(jìn)程創(chuàng)建及運行時都是無信號狀態(tài),當(dāng)結(jié)束運行時變成有信號狀態(tài)。
l 自動重置的事件(FALSE)對象,當(dāng)?shù)却晒Φ臅r候,會被修改成無信號狀態(tài)。
l 信號量對象,當(dāng)調(diào)用ReleaseSemaphore(數(shù)量加1),處于有信號狀態(tài),WaitForSingleObject會被觸發(fā)并且立行將信號數(shù)量減1.
優(yōu)點:線程同步機制速度快
缺點:容易墮入死鎖狀態(tài)多個進(jìn)程之間的線程同步會出現(xiàn)問題。(比如競爭資源、死鎖)
優(yōu)點:支持多個進(jìn)程之間的線程同步,避免死鎖
缺點:線程同步機制速度慢,線程必須從用戶模式轉(zhuǎn)為內(nèi)核模式。這個轉(zhuǎn)換需要很大的代價:來回1次需要占用x 8 6平臺上的大約1 0 0 0個C P U周期。
由于線程本身就是異步的。
線程的同步主要是通過事件(Event)內(nèi)核對象、信號量(Semaphore)內(nèi)核對象和互斥量(Mutex)內(nèi)核對象。由于都是內(nèi)核對象,所以不但可以跨線程操作,還可以跨進(jìn)程同步。
線程的同步主要是通過事件(Event)內(nèi)核對象、信號量(Semaphore)內(nèi)核對象和互斥量(Mutex)內(nèi)核對象。由于都是內(nèi)核對象,所以不但可以跨線程操作,還可以跨進(jìn)程同步。
事件(Event)內(nèi)核對象
事件分兩種類型:人工重置事件和自動重置事件,前者在觸發(fā)WaitForSingleObject以后需要手動調(diào)用ResetEvent將事件設(shè)置為無信號;而后者在觸發(fā)WaitForSingleObject以后自動將事件設(shè)置為無信號狀態(tài)。
經(jīng)常使用函數(shù):
CreateEvent,創(chuàng)建事件對象。
OpenEvent,打開已創(chuàng)建的事件對象,可以跨進(jìn)程打開。
SetEvent,將事件對象設(shè)置為有信號狀態(tài)。
ResetEvent,將事件對象設(shè)置為無信號狀態(tài)。
PulseEvent,將事件對象設(shè)置為有信號狀態(tài),然后又設(shè)置為無信號狀態(tài),此函數(shù)不經(jīng)常使用。
注意:如果上面創(chuàng)建的是人工重置事件,則兩個線程函數(shù)都將履行。如果是自動重置事件,則只能履行1個線程,且不能保證哪個線程先履行。如果要保證1個線程先履行,可以添加事件對象用來確保指定線程已履行,不能通過代碼的前后順序確保線程已履行。
信號量的使用規(guī)則:
當(dāng)前信號量資源數(shù)大于0,則標(biāo)記為有信號狀態(tài)。
當(dāng)前信號量資源數(shù)為0,則標(biāo)記為無信號狀態(tài)。
信號量資源數(shù)不能為負(fù),且最大不能超過指定數(shù)量。
經(jīng)常使用函數(shù):
CreateSemaphore,創(chuàng)建信號量對象。
OpenSemaphore,打開指定信號量對象,可以跨進(jìn)程。
ReleaseSemaphoer,資源計算加1。
這樣就可以夠保證ThreadFun1履行完了,再履行ThreadFun2,然后再履行ThreadFun1,并且保證每一個線程函數(shù)只能被調(diào)用1次.
互斥量內(nèi)核對象確保線程具有單個資源的互斥訪問權(quán)。在行動特性上,互斥量與臨界區(qū)的1樣。只不過,互斥量是內(nèi)核對象,使用時需要從用戶模式切換到內(nèi)核模式,比較耗時。但正由于是內(nèi)核對象,所以互斥量能夠跨進(jìn)程,并且能夠設(shè)置超時時間,這是它比臨界區(qū)靈活的地方。
經(jīng)常使用函數(shù):
CreateMutex,創(chuàng)建互斥量對象。
OpenMutex,打開指定互斥量對象,可以跨進(jìn)程。
ReleaseMutex,釋放互斥量,對象被標(biāo)記為有信號狀態(tài),觸發(fā)WaitForSingleObject。
互斥量和臨界區(qū)1樣,具有1個線程具有權(quán)的概念,即當(dāng)前互斥量和當(dāng)前臨界區(qū)的釋放只能由當(dāng)前線程釋放,其他線程釋放無效。由于互斥量是內(nèi)核對象,如果線程已終止,但是其所屬的互斥量仍然沒有釋放,內(nèi)核管理器會自動釋放。臨界區(qū)沒有這個功能,由于臨界區(qū)不是內(nèi)核對象,所以臨界區(qū)如果沒有正確釋放會致使死鎖。
HANDLECreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes,
BOOL bInitialOwner, LPCTSTR lpName);
bInitialOwner標(biāo)記是不是由創(chuàng)建線程具有線程所有權(quán),TRUE表示創(chuàng)建者具有,F(xiàn)ALSE表示創(chuàng)建者不具有,則是第1個調(diào)用WaitForSingleObject的線程將取得線程所有權(quán)。
兩個函數(shù)誰先調(diào)用,誰即獲得線程所有權(quán)。如果想指定線程先運行,需要判斷指定線程已履行以后再創(chuàng)建新線程,不能依托線程的代碼創(chuàng)建前后順序。
像互斥量對象一樣可以到達(dá)互斥的效果,只是互斥量功能更豐富,并且如果是簡單的資源互斥,使用臨界區(qū)的效力更優(yōu)。
臨界區(qū)(Critical Section)是1段供線程獨占式訪問的代碼,也就是說若有1線程正在訪問該代碼段,其它線程想要訪問,只能等待當(dāng)前線程離開該代碼段方可進(jìn)入,這樣保證了線程安全。他工作于用戶級(相對內(nèi)核級),在Window系統(tǒng)中CRITICAL_SECTION實現(xiàn)臨界區(qū)相干機制。
經(jīng)常使用函數(shù):
voidInitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) // 初始化臨界區(qū)
voidEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) // 進(jìn)入臨界區(qū)
voidLeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) // 離開臨界區(qū)
voidDeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) // 釋放臨界區(qū)資源
由于臨界區(qū)具有線程所有權(quán)這個概念,即進(jìn)入臨界區(qū)的線程才有權(quán)釋放臨界區(qū)。由于必須當(dāng)前線程進(jìn)入和釋放,更多的時候,臨界區(qū)是在1個函數(shù)里使用,為了確保不會由于中間退出函數(shù)致使沒有釋放,我們可以用以下方式來確保釋放。
1. 注意所有內(nèi)核對象在結(jié)束時都需要調(diào)用closeHandle()。
2. 跨線程調(diào)用MFC對象函數(shù)都是不安全的。由于MFC對象的1些函數(shù)都與TLS有關(guān)聯(lián), 所以有些調(diào)用會出錯。如UpdateData(),最好通過句柄發(fā)消息來完成相應(yīng)的功能。