1. 程式人生 > >多執行緒之mutex、semaphore區別

多執行緒之mutex、semaphore區別

多執行緒

在講多執行緒同步關鍵詞之前,先說一下單道程式。在單道程式中,每時每刻只有一個程式執行,該程式獨自佔據cpu。只有當該程式完成之後才釋放其佔據的所有資源。因為只有一個程式在執行,佔據cpu和訪問記憶體,沒有其他程式干擾。所以所有操作肯定是順序完成的,即滿足順序行、封閉性、安全性,這個時候不必擔心各種併發問題的出現。但是這隻有一個程式在執行會對資源產生很大的浪費。因為當這個唯一的程式在等待其他資源的時候,該程式就無法執行,那麼cpu在這個時候也必須等待該程式一直到該程式可以執行。這個時候cpu就什麼事情也不能做,只能空等,導致資源浪費。引入多道程式的目的是解決單道程式中cpu等待的場景。

為了解決上述問題引入了多道程式,和單道程式一樣,同一時刻也是隻能一個程式佔據一顆cpu(多核CPU)。但是和單道程式不同的是,當該程式等待其他資源的時候,它就放棄cpu或者當一個設定的時間到了放棄cpu,讓其他程式擁有cpu。該程式不可能一直佔據,如此這就避免了cpu空等的情況,讓cpu始終有程式執行。因為程式不是一直從開始執行到結束,而是中間走走停停,那就併發問題就會出現。因為當他停止的時候,它所擁有的資源或者它所需要的資源可能會被其他程式修改,這就影響程式執行的正確性。

程式的可重入

但是也並不是說所有的多執行緒同事執行就一定會導致多執行緒問題的出現。下面就簡單介紹一下可充入與執行緒安全兩個知識點。

  所謂的可重入是在單道程式時代提出來的,一個程式因為作業系統中斷被掛起,當再次進入程式執行的時候可以依舊正確執行。當我們表達一個函式可重入的時候必須部包含靜態變數和全域性變數,不能呼叫其他不可重入函式,只處理傳入的資料。從這裡也可以看出,嚴格函式式的程式設計中的函式肯定都是可重入的。

  執行緒安全性則是指在多執行緒環境下的一種現象。執行緒安全與可重入有一定的聯絡,但也並不是嚴格的因果關係。可重入肯定是執行緒安全,因為沒有公共資料的修改;但是不可重入通過一定的同步機制也可以變成執行緒安全的。

同步機制

對那些不可重入的多執行緒,我們就必須讓多個執行緒進行同步操作。

所謂同步是指的一個執行緒在訪問資料資源沒結束之前,其他執行緒不可以隨意訪問並修改資料。從這裡可以看出,同步是我們在處理多執行緒問題的目的,為了達到同步目的我們有多種實現機制。下面就開始簡單介紹幾種同步機制,通過使用同步機制保證多執行緒安全。

其中常用的同步機制主要包括訊號量互斥鎖臨界區等幾種。在此基礎上,有些語言會按照這些互斥機制研發不同的鎖機制,比如自旋鎖,可重入鎖等,但是底層的機制也是離不開這幾種原理。

1:semaphore(訊號量)範圍比較廣,semaphore可能會有多個屬性值。比如常見的生產者和消費者問題,就是多元訊號量的一種。生產者可以生產多個元素,消費者可以消費的元素必須小於生產者的生產元素個數。從此也可以看出,semaphore是允許多個執行緒進入,訪問互斥資源。除了多元訊號量之外,還存在一種二元訊號量。即只存在是與否,0與1兩種狀態。

2:mutex(互斥量)也是一種二元的鎖機制,只有是(1)和否(0)的兩個值,和二元訊號量比較相似。但是它和二元訊號量不同的是,佔有和釋放必須是同一個執行緒。比如互斥量M被執行緒A佔有,那麼釋放的時候肯定也是A執行緒釋放的。二元訊號量則不必如此,一個二元訊號量的佔有和釋放可以是不同執行緒。相應的內容也可以移步看一下wiki解釋--Synchronization 。mutex是可以用於程序也可以用於執行緒的同步機制,見wiki(refers to the problem of ensuring that no two processes or threads (henceforth referred to only as processes) are in their critical section at the same time.)

3:critical section(臨界區)是一種比互斥量更嚴格的互斥訪問機制。只允許一個程序訪問,不可能兩個程序進行競爭。一個程序建立了臨界區以後,其他程序是不可能獲取到該臨界區的進入權利的,但是允許程序內的多個執行緒競爭。在某些情況下我們可以使用mutex機制來保護critical section。我們可以考慮java中臨界區,java是一個程序下的多執行緒。

4:對於lock,其實它是一種同步機制的統稱,即鎖機制。通過鎖機制實現執行緒之間的同步,但是鎖機制的實現是有很多種。除了上述的幾種還有其他比如條件變數等。在其他語言中也有其他鎖的機制,比如.net中的monitor,其實monitor是lock的一種實現。不同語言上對於鎖機制的實現就可能有些許差別。雖然有時候說的是一種鎖,但是其底層的具體實現機制就有可能是臨界區或者訊號量機制等。

在鎖之中有種特殊鎖,讀寫鎖。簡單理解是在讀的時候可以共享讀,但不可以寫;在寫的時候只能獨佔式的寫。像這種鎖,在一些資料庫中被廣泛使用。通過讀寫鎖的使用,讓資料庫的讀的效能得到提升。

問題

雖然有各種鎖的出現,讓多執行緒問題得到一部分的解決,但是僅僅通過語言或者系統層面給出解決方式是不夠的。因為在編譯器優化、在CPU都會讓高階語言編譯成低階語言,在呼叫指令的時候出現亂序的問題。在這個時候雖然程式中採用了鎖的機制,但是依舊會出現多執行緒執行亂序問題,導致程式執行結果失敗。