Linux 多線程(二)線程安全:線程安全、互斥、死鎖、同步

  1. 線程安全
  2. 互斥
  3. 死鎖
  4. 同步

線程安全

所謂線程安全,其實就是當多個線程對臨界資源進行爭搶訪問的時,不會造成數據二義或者邏輯混亂的情況(通常情況下對全局變量和靜態變量進行操作時在會出現)

常見的線程安全的情況:

  • 每個線程對全局變量或者靜態變量只有讀取的權限,而沒有寫入的權限,一般來說這些線程是安全的
  • 類或者接口對於線程來說都是原子操作
  • 多個線程之間的切換不會導致該接口的執行結果存在二義性

常見的線程不安全的情況:

  • 不保護共享變量的函數
  • 函數狀態隨着被調用,狀態發生變化的函數
  • 返回指向靜態變量指針的函數
  • 調用線程不安全函數的函數

線程安全的實現:

同步:通過條件判斷實現對臨界資源訪問的合理性
互斥:通過同一時間對臨界資源訪問的唯一性實現臨界資源訪問的安全性


互斥

互斥:通過同一時間對臨界資源訪問的唯一性實現臨界資源訪問的安全性

如果需要實現互斥,就需要藉助互斥鎖

互斥鎖:互斥鎖本身是一個只有0和1的二進制計數器,描述了一個臨界資源當前的訪問狀態,所有線程在訪問臨界資源前都需要先判斷當前臨界資源的狀態是否允許訪問,如果不允許則讓線程等待,如果允許則讓線程訪問資源,但是訪問資源時需要加鎖修改臨界資源的狀態爲不可訪問,保證一次只能有一個線程訪問臨界資源,訪問結束後再解鎖,使臨界資源恢復成可訪問的狀態。

互斥鎖雖然本身也是一個臨界資源,如果自己的操作都不安全,那怎麼能保證其他資源的安全?所以它自身的加鎖解鎖操作保證了原子性。

所以互斥鎖與CPU中寄存器進行數據交換,交換當前的狀態,確保操作能夠一次完成。

在這裏插入圖片描述

操作:

1.定義互斥鎖變量:

pthread_mutex_t mutex;

2.初始化互斥鎖:

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr)

attr:互斥鎖屬性,如果爲NULL則爲默認屬性
頭文件:#include <pthread.h>
返回值:成功返回0,失敗返回其他

3.訪問臨界資源之前加鎖:

int pthread_mutex_lock(pthread_mutex_t *mutex);
//阻塞加鎖,如果當前互斥鎖已被鎖定,則會阻塞當前線程,直到該互斥鎖可以用

int pthread_mutex_trylock(pthread_mutex_t *mutex);
//非阻塞加鎖,如果當前互斥鎖已被鎖定,則不會阻塞,直接返回互斥鎖狀態

4.訪問臨界資源結束後解鎖:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

5.銷燬互斥鎖:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

死鎖

死鎖即當多個線程對鎖資源爭搶訪問,但是由於推進的順序不當,導致互相等待最終導致程序流程無法繼續推進,導致了該資源處於永久等待狀態,這就是死鎖。

死鎖產生的必要條件:

互斥條件:一個資源每次只能被一個執行流使用
請求與保持條件:一個執行流因請求資源而阻塞時,對已獲得的資源保持不放
不剝奪條件:一個執行流已獲得的資源,在末使用完之前,不能強行剝奪
循環等待條件:若干執行流之間形成一種頭尾相接的循環等待資源的關係

死鎖的預防:

1.加鎖順序一致
2.避免鎖未釋放的場景
3.資源一次性分配
4.避免死鎖算法

避免死鎖的算法:

死鎖檢測算法
銀行家算法
(過段時間會寫一個關於這兩個算法的博客)


同步

同步:通過條件判斷實現對臨界資源訪問的合理性
如果要實現同步,就需要藉助條件變量

條件變量:線程在滿足資源的訪問條件的時候纔去訪問臨界資源,否則就會掛起線程,直到條件滿足才喚醒線程。

條件變量提供了是線程等待和喚醒的接口和一個線程的等待隊列,所以需要我們自己來完成訪問條件的判斷。

操作:

1.定義條件變量:

pthread_cond_t cond

2.初始化條件變量:

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t 
*cond_attr)
//動態創建

pthread_cond_t cond=PTHREAD_COND_INITIALIZER
//靜態創建

3.使線程掛起:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
//條件等待

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
//計時等待,如果時間內沒有完成條件則報錯返回

4.喚醒線程:

 int pthread_cond_signal(pthread_cond_t *cond);

5.銷燬條件變量:

int pthread_cond_destroy(pthread_cond_t *cond)

爲什麼 pthread_cond_wait需要使用互斥鎖呢?

  • 條件等待是線程間同步的一種手段,如果只有一個線程,條件不滿足,一直等下去都不會滿足,所以必須要有一個線程通過某些操作,改變共享變量,使原先不滿足的條件變得滿足,並且友好的通知等待在條件變量上的線程。
  • 條件不會無緣無故的突然變得滿足了,必然會牽扯到共享數據的變化。所以一定要用互斥鎖來保護。沒有互斥鎖就無法安全的獲取和修改共享數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章