面試筆記(一):系統程式設計(執行緒同步——互斥鎖、讀寫鎖、訊號量、條件變數)
1.linux下執行緒同步的方式(轉自:https://blog.csdn.net/Shannon_ying/article/details/51280623、
https://blog.csdn.net/q_l_s/article/details/44117929)
執行緒的最大特點是資源的共享性,但資源共享中的同步問題是多執行緒程式設計的難點。linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數和訊號量。
(1)互斥鎖(mutex):通過鎖機制實現執行緒間的同步。
1.初始化鎖。在Linux下,執行緒的互斥量資料型別是pthread_mutex_t。在使用前,要對它進行初始化。
靜態分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
動態分配:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
2. 加鎖。對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,呼叫執行緒會阻塞,直到互斥量被解鎖。
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
- 1
- 2
int pthread_mutex_unlock(pthread_mutex_t *mutex );
4.銷燬鎖。鎖在是使用完成後,需要進行銷燬以釋放資源。int pthread_mutex_destroy(pthread_mutex *mutex);
(2)讀寫鎖
讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。互斥量要麼是鎖住狀態要麼是不加鎖狀態,而且一次只有一個執行緒可以對其加鎖。
讀寫鎖可以由三種狀態:讀模式下加鎖狀態、寫模式下加鎖狀態、不加鎖狀態。一次只有一個執行緒可以佔有寫模式的讀寫鎖,但是多個執行緒可以同時佔有讀模式的讀寫鎖。
在讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的執行緒都會被阻塞。當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的執行緒都可以得到訪問權,但是如果執行緒希望以寫模式對此鎖進行加鎖,它必須阻塞直到所有的執行緒釋放讀鎖。雖然讀寫鎖的實現各不相同,但當讀寫鎖處於讀模式鎖住狀態時,如果有另外的執行緒試圖以寫模式加鎖,讀寫鎖通常會阻塞隨後的讀模式鎖請求。這樣可以避免讀模式鎖長期佔用,而等待的寫模式鎖請求一直得不到滿足。
讀寫鎖非常適合於對資料結構讀的次數遠大於寫的情況。當讀寫鎖在寫模式下時,它所保護的資料結構就可以被安全地修改,因為當前只有一個執行緒可以在寫模式下擁有這個鎖。當讀寫鎖在讀狀態下時,只要執行緒獲取了讀模式下的讀寫鎖,該鎖所保護的資料結構可以被多個獲得讀模式鎖的執行緒讀取。
讀寫鎖也叫做共享-獨佔鎖,當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當他以寫模式鎖住時,它是以獨佔模式鎖住的。
(3)條件變數(cond)與互斥鎖不同,條件變數是用來等待而不是用來上鎖的。條件變數用來自動阻塞一個執行緒,直到某特殊情況發生為止。通常條件變數和互斥鎖同時使用。條件變數分為兩部分: 條件和變數。條件本身是由互斥量保護的。執行緒在改變條件狀態前先要鎖住互斥量。條件變數使我們可以睡眠等待某種條件出現。條件變數是利用執行緒間共享的全域性變數進行同步的一種機制,主要包括兩個動作:一個執行緒等待”條件變數的條件成立”而掛起;另一個執行緒使”條件成立”(給出條件成立訊號)。條件的檢測是在互斥鎖的保護下進行的。如果一個條件為假,一個執行緒自動阻塞,並釋放等待狀態改變的互斥鎖。如果另一個執行緒改變了條件,它發訊號給關聯的條件變數,喚醒一個或多個等待它的執行緒,重新獲得互斥鎖,重新評價條件。如果兩程序共享可讀寫的記憶體,條件變數可以被用來實現這兩程序間的執行緒同步。
1. 初始化條件變數。
靜態態初始化,pthread_cond_t cond = PTHREAD_COND_INITIALIER;
動態初始化,int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
2. 等待條件成立。釋放鎖,同時阻塞等待條件變數為真才行。timewait()設定等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個執行緒wait)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
- 1
- 2
3.啟用條件變數。pthread_cond_signal,pthread_cond_broadcast
(啟用所有等待執行緒)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
4.清除條件變數。無執行緒等待,否則返回EBUSY
int pthread_cond_destroy(pthread_cond_t *cond);
1. 訊號量初始化。
int sem_init (sem_t *sem , int pshared, unsigned int value);
- 1
這是對由sem指定的訊號量進行初始化,設定好它的共享選項(linux 只支援為0,即表示它是當前程序的區域性訊號量),然後給它一個初始值VALUE。
2. 等待訊號量。給訊號量減1,然後等待直到訊號量的值大於0。
int sem_wait(sem_t *sem);
- 1
- 釋放訊號量。訊號量值加1。並通知其他等待執行緒。
int sem_post(sem_t *sem);
- 1
- 銷燬訊號量。我們用完訊號量後都它進行清理。歸還佔有的一切資源。
int sem_destroy(sem_t *sem);