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