歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux多線程處理知識

Linux多線程處理知識

日期:2017/2/28 16:45:41   编辑:Linux教程

多線程的競爭條件:

當一個線程訪問一個數據結構的時候,另一個線程也訪問同一個數據結構,這時就出現了競爭條件——兩個線程(也可能是多個)競爭對同一個資源的訪問。

當其中一個線程處理到一部分的時候,另外的線程可能進入了對同一數據的處理,而且出於調度的原因,它運行的比前一個更快;這時,同樣的處理可能就出現了多次。

例如,一個代表任務列表的單向鏈表(隊列),一個線程從當前元素中讀出了下一個任務,並發現下一個任務不是空,准備將此一個任務置為NULL並執行任務;這時,調度使得這個線程停下來,另一個線程也從當前元素中讀出了下一個任務,當然下個任務仍然不是空值,這個線程也將要執行下一個任務。這樣,在某些不幸的情況下,這個任務被執行了兩次。

更為不幸的情況下,執行任務的過程中線程被中斷,此時另一個線程釋放了任務的內存,那麼執行中的線程會導致段錯誤。在比較“幸運”的情況下,這些事情可能從來也不發生;但是當程序運行在負荷很高的系統中時,這個bug就會凸現出來。

互斥鎖:

為了排除競爭條件的影響,應該使一些操作變成“原子的”——這些操作既不能分割也不能中斷。

當一個線程鎖定了互斥鎖後,其他線程也要求鎖定這個互斥鎖的時候,就會被阻塞;直到前面的線程解除鎖定後,其他線程才可以解除阻塞恢復運行。GNU/Linux保證線程在鎖定互斥鎖的過程中不會發生競爭條件,只有一個線程可以鎖定互斥鎖,其他線程的鎖定請求都會被阻塞。

創建互斥鎖(Mutex),需要:

創建一個pthread_mutex_t類型的變量,將其指針傳入函數pthread_mutex_init中;該函數的第二個參數是指向一個mutex屬性對象(這個對象用來指定mutex的屬性)的指針。mutex對象只能初始化一次。

更簡單的辦法是,使用PTHREAD_MUTEX_INITIALIZER來獲得默認屬性的mutex對象:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

使用mutex:

調用pthread_mutex_lock函數,如果mutex對象未被鎖定,則此函數立即返回;如果已被鎖定,則此函數阻塞此線程的執行,直到mutex被解除鎖定。每次解除鎖定後,只有一個線程可以解除阻塞恢復執行,其他調用線程都會繼續阻塞。選定的解除阻塞的線程是不可預知的。調用pthread_mutex_unlock函數,可以解除調用線程對mutex對象的鎖定。在鎖定mutex的線程中,必須調用此函數以解除鎖定。

mutex提供了解決競爭條件的方案,但是它帶來了一種新的bug——死鎖(deadlock)。

所謂死鎖,就是說一個線程在等待永遠不會發生的條件。

一個簡單的死鎖:一個線程鎖定它自己已經鎖定的mutex。這時程序的表現依賴於mutex的類型:

快速排他鎖(fast mutex)——導致死鎖。

遞歸排他鎖(recursive mutex)——不導致死鎖。同一個線程可以安全的多次鎖定同一個已鎖定的mutex,但是鎖定的次數會被記錄下來,該線程還必須調用相應次數的pthread_mutex_unlock才能真正解除對mutex的鎖定。

檢錯排他鎖(error-checking mutex)——GNU/Linux視此操作為死鎖,但是對鎖定的mutex調用pthread_mutex_lock函數,函數立即返回錯誤碼EDEADLK。

mutex的鎖定函數會阻塞,有時需要一個不阻塞就能知道mutex是否已鎖定的函數,以在發現mutex已鎖的情況下去完成其他工作並在以後再來檢查。這樣的函數是:pthread_mutex_trylock(),如果傳入的mutex已經被其他線程鎖定,那麼這個函數返回錯誤碼EBUSY;如果未被鎖定,此函數會鎖定mutex,並返回0。這個函數不會阻塞。

Copyright © Linux教程網 All Rights Reserved