進程是Linux資源分派的對象,Linux會為進程分配虛擬記憶體(4G)和檔案控制代碼等資源,是一個靜態概念。線
程是CPU調度的對象,是一個動態概念。一個進程之中至少包含有一個或者多個線程。這些線程共用該進程空間的記憶體和檔案控制代碼資源,多個線程競爭地獲得這
些資源。為了防止多個線程訪問資源的不一致性,多線程編程一個很重要的任務就是控制好線程同步。本文簡單介紹一下Linux的同步對象和使用時的一些注意
事項。
1、互斥量(Mutex)
互斥量本質上講是一把鎖,該鎖保護一個或者一些資源(記憶體或者檔案控制代碼等資料)。一個線程如果需要訪問該資源必須要獲得互斥量,並對其加鎖。這時如果其他
線程如果想訪問該資源也必須要獲得該互斥量,但是鎖已經加鎖,所以這些進程只能阻塞,直到獲得該鎖的線程解鎖。這時阻塞的線程裡面有一個線程獲得該互斥量
並加鎖,獲准訪問該資源。其他的線程繼續阻塞,周而復始。
Linux互斥量控制代碼為pthread_mutex_t。可以以PTHREAD_MUTEX_INITIALIZER初始化一個互斥量,或者調用如下函數動態進行初始化:
#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
銷毀一個互斥量調用如下函數:
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);
對一個互斥量加鎖和解鎖函數如下:
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);
當前線程調用pthread_mutex_lock函數時,如果該互斥量未加鎖,則當前線程獲得該互斥量並解鎖,該函數返回;如果當前該互斥量已經加鎖,則該函數將會阻塞,直到該互斥量解鎖,當前線程獲得該互斥量,並加鎖返回。
pthread_mutex_trylock如果互斥量為未加鎖,則當前線程將會獲得該互斥量並加鎖。當互斥量為加鎖狀態,該函數將會立即返回錯誤EBUSY,不會阻塞當前線程。
互斥量的解鎖函數為pthread_mutex_unlock,這樣將會釋放互斥量資源。
另外注意一個問題就是互斥量死結(dead
lock)的問題。當一個互斥量的時候,不會發生互斥量的問題。當有多個互斥量的時候,有可能發生死結。例如:有互斥量A,B。假如第一個線程獲得互斥量
A,並加鎖,這時他嘗試獲得互斥量B,但是互斥量B已經加鎖,該線程被阻塞,等待互斥量B。同時另外一個線程先獲得互斥量B,並已加鎖。這時嘗試獲得互斥
量A,發現互斥量A已經加鎖,則阻塞該線程,等待互斥量A。這樣出現兩個線程互相等待對方已經獲得的訊號量的問題,都處於阻塞狀態,出現死結。那麼怎樣解
決這種死結問題呢?那就是線程以同樣的順序獲得互斥量。第一個線程先獲得互斥量A,再獲得互斥量B;第二個線程也以同樣的順序獲得互斥量。這樣就不會出現
死結的狀態了。
但是在一些結構複雜的程式中,很難保證以同樣的順序獲得互斥量,那麼怎樣解決死結問題呢?就是以pthread_mutex_trylock來嘗試獲得互斥量,如果不能獲得互斥量,則釋放已經持有的互斥量。過段時間,再次進行同樣的嘗試,這樣可以避免死結。
原文