天天看點

Windows線程的同步與互斥

系統中的所有線程都必須擁有對各種系統資源的通路權,這些資源包括記憶體堆棧,序列槽,檔案,視窗和許多其他資源。如果一個線程需要獨占對資源的通路權,那麼其他線程就無法完成它們的工作。反過來說,也不能讓任何一個線程在任何時間都能通路所有的資源。如果在一個線程從記憶體塊中讀取資料時,另一個線程卻想要将資料寫入同一個記憶體塊,那麼這就像你在讀一本書時另一個人卻在修改書中的内容一樣。這樣,書中的内容就會被搞得亂七八糟,結果什麼也看不清楚。

線程需要在下面兩種情況下互相進行通信:

1.當有多個線程通路共享資源而不使資源被破壞時。

2.當一個線程需要将某個任務已經完成的情況通知另外一個或多個線程時。

Windows下線程同步互斥常用的幾種方法:

1)CriticalSection: 臨界區

适用範圍: 單一程序的各線程之間用來排它性占有

特性: 不是核心對象,快速而有效。無法監測是否被線程放棄。如果在Critical Sections中間突然程式crash或是exit而沒有調用LeaveCriticalSection,則結果是該線程所對應的核心不能被釋放,該線程成為死線程。

函數: EnterCriticalSection LeaveCriticalSection

很好的封裝:

Windows線程的同步與互斥
Windows線程的同步與互斥

調用:CritSect sect; Lock lock(sect);

2)Mutex: 互斥核心對象

适用範圍: 不同線程之間用來排它性占有

特性: 核心對象,哪個線程擁有mutex,那麼該mutex的ID和此線程的ID一樣。

函數: CreateMutex ReleaseMutex

3)Event: 事件核心對象

适用範圍: 用來控制對象信号的接收,常與信号系統結合起來

特性: 核心對象,有兩種不同類型的事件對象。一種是人工重置的事件,另一種是自動重置的事件。當人工重置的事件得到通知時(signaled),等待該事件的所有線程均變為可排程線程。當一個自動重置的事件得到通知時,等待該事件的線程中隻有一個線程變為可排程線程。 

Microsoft為自動重置的事件定義了應該成功等待的副作用規則,即當線程成功地調用wait函數等待到該對象時,自動重置的事件就會自動重置到未通知狀态(nonsignaled)。通常沒有必要為自動重置的事件調用ResetEvent()函數,因為系統會自動對事件進行重置。但是,Microsoft沒有為人工重置的事件定義成功等待的副作用,是以需要調用ResetEvent()函數将Event設定為未通知狀态(nonsignaled)。當調用SetEvent觸發Auto-reset的Event條件時,如果沒有被條件阻塞的線程,那麼此函數仍然起作用,條件變量會處在觸發狀态(和Linux的pthread_cond_signal()不同)。

函數: CreateEvent OpenEvent PulseEvent SetEvent ResetEvent 

4)Semaphore: 信号核心對象

适用範圍: 用來限制資源占用

特性: 核心對象,沒有擁有者,任何線程都可釋放。信号量(Semaphore)核心對象對線程的同步方式與前面幾種方法不同,它允許多個線程在同一時刻通路同一資源,但是需要限制在同一時刻通路此資源的最大線程數目。在用 CreateSemaphore()建立信号量時即要同時指出允許的最大資源計數和目前可用資源計數。一般是将目前可用資源計數設定為最大資源計數,每增加一個線程對共享資源的通路,目前可用資源計數就會減1,隻要目前可用資源計數是大于0的,就可以發出信号量信号。但是目前可用計數減小到0時則說明目前占用資源的線程數已經達到了所允許的最大數目,不能在允許其他線程的進入,此時的信号量信号将無法發出。線程在處理完共享資源後,應在離開的同時通過 ReleaseSemaphore()函數将目前可用資源計數加1。在任何時候目前可用資源計數決不可能大于最大資源計數。

函數: CreateSemaphore OpenSemaphore ReleaseSemaphore

等待函數

1)WaitForSingleObject()

等待函數可使線程自願進入等待狀态,直到一個特定的核心對象變為已通知狀态為止。

Windows線程的同步與互斥
Windows線程的同步與互斥

2)WaitForMultipleObjects()

WaitForMultipleObjects與WaitForSingleObject函數很相似,差別在于它允許調用線程同時檢視若幹個核心對象的已知狀态。WaitForMultipleObjects函數的傳回值告訴調用線程,為什麼它會被重新排程。可能的傳回值是WAIT_FAILED和WAIT_TIMEOUT,這兩個值的作用是很清楚的。如果為fWaitAll參數傳遞TRUE,同時所有對象均變為已通知狀态,那麼傳回值是WAIT_OBJECT_0。如果為fWaitAll傳遞FALSE,那麼一旦任何一個對象變為已通知狀态,該函數便傳回。在這種情況下,你可能想要知道哪個對象變為已通知狀态。傳回值是WAIT_OBJECT_0與(WAIT_OBJECT_0+dwCount-1)之間的一個值。換句話說,如果傳回值不是WAIT_TIMEOUT,也不是WAIT_FAILED,那麼應該從傳回值中減去WAIT_OBJECT_0。産生的數字是作為第二個參數傳遞給WaitForMultipleObjects的句柄數組中的索引。該索引說明哪個對象變為已通知狀态。下面是說明這一情況的一些示例代碼:

Windows線程的同步與互斥
Windows線程的同步與互斥

3)SingleObjectAndWait()

DWORD SingleObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD dwMilliseconds,BOOL fAlertable);

函數用于在單個原子方式的操作中發出關于核心對象的通知并等待另一個核心對象:hObjectToSignal參數必須辨別一個互斥對象、信号對象或事件對象。hObjectToWaitOn參數用于辨別下列任何一個核心對象:互斥對象、信标、事件、定時器、程序、線程、作業、控制台輸入和修改通知。與平常一樣,dwMilliseconds參數指明該函數為了等待該對象變為已通知狀态,應該等待多長時間,而fAlertable标志則指明線程等待時該線程是否應該能夠處理任何已經排隊的異步過程調用。

4)MsgWaitForMultipleObjects(Ex)

MsgWaitForMultipleObjects和MsgWaitForMultipleObjectsEx這些函數與WaitForMultipleObjects函數十分相似。差别在于它們允許線程在核心對象變成已通知狀态或視窗消息需要排程到調用線程建立的視窗中時被排程。

建立視窗和執行與使用者界面相關的任務的線程,應該調用MsgWaitForMultipleObjectsEx函數,而不應該調用WaitForMultipleObjects函數,因為後面這個函數将使線程的使用者界面無法對使用者作出響應。

Windows下的生産者消費者問題:

Windows線程的同步與互斥

View Code

參考資料: 

<a href="http://msdn2.microsoft.com/en-us/library/ms686360(VS.85).aspx">http://msdn2.microsoft.com/en-us/library/ms686360(VS.85).aspx</a>

<a href="http://www.cppblog.com/mzty/archive/2008/07/29/57470.html">http://www.cppblog.com/mzty/archive/2008/07/29/57470.html</a>

    本文轉自阿凡盧部落格園部落格,原文連結:http://www.cnblogs.com/luxiaoxun/archive/2012/10/10/2717765.html,如需轉載請自行聯系原作者

繼續閱讀