標籤:互斥 鎖 多線程 同步 記憶體
多線程
一個進程在同一時刻只能做一件事,而多個線程卻可以同時執行,每個線程處理各自獨立的任務。多線程有很多好處:
- 簡化處理非同步事件的代碼
- 實現記憶體和檔案描述符的共用
- 改善程式的輸送量
- 改善回應時間
互斥鎖
互斥鎖:互斥鎖通過鎖機制來實現線程間的同步,在同一時刻通常只允許一個關鍵區段的代碼
當多個線程式控制制相同的記憶體時,對於讀寫操作的時間差距就有可能會導致資料的不同步,就很清晰的說明了這種情況:
對於線程A、B此時對同一塊記憶體進行操作,但是由於操作幾乎是同時進行的,假設當線程A讀入資料i之後,在對資料 i 自加1的同時線程B將原來記憶體中的資料 i 讀出,然後線程A將自加後的資料再寫入記憶體中,同時線程B對讀取到的 i 加1,最後將其寫入記憶體,這個程式原本是想通過兩個線程對變數 i 進行兩次自加運算最後預想的結果應當是7,最後卻得到異常的結果。對於這種臨界變數操作時就有可能導致意外的結果,因而出現了互斥鎖和條件變數來保證資料只被一個線程操作。
下面程式的運行結果也很好的說明了多線程操作同一變數不穩定性:
#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<errno.h>int a = 0; //用作要改變的全域變數pthread_mutex_t mutex; //互斥鎖變數void thread1(void *arg){ //pthread_mutex_lock (&mutex); //加鎖 printf("thread1 a = %d\n", a); a++; //pthread_mutex_unlock (&mutex);}void thread2(void *arg){ //pthread_mutex_lock (&mutex); printf("thread2 a = %d\n", a); a++; //pthread_mutex_unlock (&mutex);}void thread3(void *arg){ //pthread_mutex_lock (&mutex); printf("thread3 a = %d\n", a); a++; //pthread_mutex_unlock (&mutex);}int main(void){ pthread_t tid1, tid2, tid3; //用於接收線程ID int err; //出錯碼 void * tret; //傳回值 //建立三個線程 pthread_create (&tid1, NULL, (void *)thread1, NULL); pthread_create (&tid2, NULL, (void *)thread2, NULL); pthread_create (&tid3, NULL, (void *)thread3, NULL); //等待輔線程結束 err = pthread_join(tid1, &tret); if(err != 0) { perror("join"); } err = pthread_join(tid2, &tret); if(err != 0) { perror("join"); } err = pthread_join(tid3, &tret); if(err != 0) { perror("join"); } printf("result a: %d\n", a); //查看最後的結果 exit(0);}
多次執行結果如下:
發現幾次的執行結果並不相同,本來程式是通過三個線程對同一個全域變數進行3次加1的操作結果應當為0, 1,2,3,由於線程間資料不同步所以導致了這樣的結果。
當前我們將程式中各個thread函數中的注釋取消之後就會得到這樣的結果:
這裡就體現了互斥鎖來保證線程間資料的同步。
使用互斥鎖應當注意:
- 使用之前必須進行初始化
- 加鎖時,若鎖變數已經被鎖住,則當前嘗試加鎖的線程就會阻塞,直到互斥鎖被其他線程釋放
- 調用解鎖函數的線程必須是給互斥鎖加鎖的線程
- 當一個線程試圖以另一個線程相反的順序鎖住互斥量的時候,就有可能出現死結
- 如果線程試圖對一個互斥量加鎖兩次,那麼它自身就會陷入死結狀態
死結:指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死結狀態或系統產生了死結,這些永遠在互相等待的進程稱為死結進程
條件變數
條件變數:是利用線程間共用的全域變數進行同步的一種機制
使用條件變數要注意:
- 使用之前要初始化
- 條件變數通常是在互斥鎖的保護下求值,若條件運算式為假,那麼線程基於條件變數阻塞。
- 線程被條件變數阻塞後,可以通過pthread_cond_signal和pthread_cond_broadcast啟用,pthread_cond_signal啟用按照入隊順序啟用一個等待線程,pthread_cond_broadcast則啟用所有的等待線程
- 當一個條件變數不再使用時,需要將其清除,清除函數:pthread_cond_destory,只有在沒有線程等待該條件變數的時候才能清除這個條件變數,否則返回EBUSY。
- 調用pthread_cond_wait()前必須有本線程加鎖,防止多個線程同時請求pthread_cond_wait()。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
多線程之互斥鎖、條件變數