因為執行緒獨自擁有的只有棧,其他的區域執行緒共同擁有。並且對共享區域的操作並不都是原子的。對共享區域的操作順序又是不確定的。就像建立兩個檔案描述符同時指向
同一檔案,並且連續向檔案中寫入那麼寫的東西可能是亂七八糟的。這時就需要執行緒對共享區的同步。
而另一種情況是,多個執行緒的指令執行順序需要同步。(這與訪問區域無關,純粹是指令順序的方面)
臨界區:
保證在某一時刻只有乙個執行緒能訪問資料的簡便辦法。在任意時刻只允許乙個執行緒對共
享資源進行訪問。如果有多個執行緒試圖同時訪問臨界區,那麼 在有乙個執行緒進入後其他所
有試圖訪問此臨界區的執行緒將被掛起,並一直持續到進入臨界區的執行緒離開。臨界區在被釋
放後,其他執行緒可以繼續搶占,並以此達到用原子方式操作共享資源的目的。
臨界區的選定因盡可能小,如果選定太大會影響程式的並行處理效能。
那麼下面介紹一些執行緒同步的方法。
1> 互斥量
互斥量有兩種狀態 鎖被占用 所空閒
int pthread_mutex_destroy(pthread_mutex_t *mutex); //銷毀乙個鎖 如果malloc分配的鎖 要在free前銷毀 否則可能會出現鎖洩露
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);//初始化乙個鎖 attr == null則用預設屬性
int pthread_mutex_lock(pthread_mutex_t *mutex); //加鎖 如果鎖被占用則函式阻塞知道鎖被釋放
int pthread_mutex_trylock(pthread_mutex_t *mutex); //嘗試加鎖 如果鎖是空閒的 則加鎖 否則返回錯誤編號而不阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex); //解鎖
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);//限時阻塞加鎖函式
如果在時間耗盡內還未取得鎖那麼返回錯誤編號 可以讓程式不會永遠阻塞
返回值:成功返回0 出錯返回錯誤編號
如果鎖是靜態分配的還可以直接賦值 pthread_mutex_initializer;(malloc則不能直接這樣做必須用初始化函式)
pthread_mutex_t 鎖結構資料型別 這個結構貌似是透明的上面一組函式用於操作它
死鎖:
1.同乙個執行緒在擁有a鎖的情況下再次請求獲得a鎖
2.執行緒一擁有a鎖,請求獲得b鎖;執行緒二擁有b鎖,請求獲得a鎖
死鎖導致的結果是什麼?
解決方法 用兩個鎖時可以按相同的順序加鎖 如果鎖的數量過多那麼程式會變得異常複雜
2>讀寫鎖
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//可以解 讀鎖 或 寫鎖
返回值 成功返回0 否則 返回錯誤碼
用法與互斥量類似不再贅述
互斥量有兩種狀態,而讀寫鎖有三種狀態。 讀鎖 寫鎖 空閒
若寫鎖已加 則試圖加鎖的執行緒阻塞
若讀鎖已加 則加讀鎖成功 加寫鎖阻塞知道所有讀鎖解除(可以多個程序同時擁有讀鎖)
隨具體實現不同
若對讀鎖已加 請求寫鎖 再請求加讀鎖則可能阻塞 這樣就防止寫鎖總是得不到請求而長期阻塞(未能證實 redhat 6.0)
加讀鎖的數量可能有限 (未能證實 redhat6.0 )
帶有超時的讀寫鎖
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock,
const struct timespec *restrict abs_timeout);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock,
const struct timespec *restrict abs_timeout);
*避免加鎖時永久阻塞
3>條件變數
互斥量存在的問題:從本質上說互斥量就是一把鎖,互斥量序列執行,能確保每次只有乙個執行緒訪問。互斥量是執行緒程式必需的工具,但它們並非萬能的。例如,如果執行緒正在輪詢等待共享資料內某個條件出現,那會發生什麼呢?它可以重複對互斥物件鎖定和解鎖,每次都會檢查共享資料結構,以查詢某個值。但這是在浪費時間和資源,而且這種繁忙查詢的效率非常低。同樣,在每次檢查之間讓執行緒短暫地進入睡眠,比如睡眠3s,但是因此執行緒**就無法最快作出響應。
問題的解決: 條件變數通過允許執行緒阻塞和等待另乙個執行緒傳送訊號的方法彌補了互斥鎖的不足,條件變數常和互斥鎖一起使用。使用時,條件變數被用來阻塞乙個執行緒,當條件不滿足時,執行緒往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個執行緒改變了條件變數,它將通知相應的條件變數喚醒乙個或多個正被此條件變數阻塞的執行緒。這些執行緒將重新鎖定互斥鎖並重新測試條件是否滿足。
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
pthread_ pthread_cond_initializer; //用於初始化靜態的條件變數
條件變數的關鍵在於int pthread_cond_wait
這個api執行了三個動作
解鎖 阻塞(這兩個是原子的)加鎖
前兩個動作解除了 條件判斷與阻塞之間的競爭條件。
Linux多執行緒,執行緒同步
5 執行緒私有資料 程序內的所有執行緒共享程序的資料空間,因此全域性變數為所有執行緒所共有。但有時執行緒也需要儲存自己的私有資料,這時可以建立執行緒私有資料 thread specific date tsd 來解決。例如我們常見的變數 errno 它返回標準的出錯資訊。它顯然不能是乙個區域性變數,幾...
Linux多執行緒 執行緒同步
執行緒同步的概念 即當有乙個執行緒在對記憶體進行操作時,其他執行緒都不可以對這個記憶體位址進行操作,直到該執行緒完成操作,其他執行緒才能對該記憶體位址進行操作,而其他執行緒又處於等待狀態。在一般情況下,建立乙個執行緒是不能提高程式的執行效率的,所以要建立多個執行緒。但是多個執行緒同時執行的時候可能呼...
Linux執行緒同步
1.概要 執行緒的同步,發生在多個執行緒共享相同記憶體的時候,這時,要保證每個執行緒在每個時刻看到的共享資料是一致的。如果每個執行緒使用的變數都是其他執行緒不會使用的 read write 或者變數是唯讀的,就不存在一致性問題。但是,如果兩個或兩個以上的執行緒可以read write乙個變數時,就需...