Linux系统编程----16(线程同步,互斥量 mutex,互斥锁的相关函数,死锁,读写锁)
同步概念
所謂同步,即同時起步,協調一致。不同的對象,對“同步”的理解方式略有不同。如,設備同步,是指在兩 個設備之間規定一個共同的時間參考;數據庫同步,是指讓兩個或多個數據庫內容保持一致,或者按需要部分保持 一致;文件同步,是指讓兩個或多個文件夾里的文件保持一致。等等
而,編程中、通信中所說的同步與生活中大家印象中的同步概念略有差異。“同”字應是指協同、協助、互相 配合。主旨在協同步調,按預定的先后次序運行。
線程同步
同步即協同步調,按預定的先后次序運行。
線程同步,指一個線程發出某一功能調用時,在沒有得到結果之前,該調用不返回。同時其它線程為保證數據 一致性,不能調用該功能。
舉例 1: 銀行存款 5000。柜臺,折:取 3000;提款機,卡:取 3000。剩余:2000
舉例 2: 內存中 100 字節,線程 T1 欲填入全 1, 線程 T2 欲填入全 0。但如果 T1 執行了 50 個字節失去 cpu,T2 執行,會將 T1 寫過的內容覆蓋。當 T1 再次獲得 cpu 繼續 從失去 cpu 的位置向后寫入 1,當執行結束,內存中的 100 字節,既不是全 1,也不是全 0。
產生的現象叫做“與時間有關的錯誤”(time related)。為了避免這種數據混亂,線程需要同步。
“同步”的目的,是為了避免數據混亂,解決與時間有關的錯誤。實際上,不僅線程間需要同步,進程間、信 號間等等都需要同步機制。
因此,所有“多個控制流,共同操作一個共享資源”的情況,都需要同步。
數據混亂原因
互斥量 mutex
Linux 中提供一把互斥鎖 mutex(也稱之為互斥量) 。
每個線程在對資源操作前都嘗試先加鎖,成功加鎖才能操作,操作結束解鎖。
但,應注意:同一時刻,只能有一個線程持有該鎖。
當 A 線程對某個全局變量加鎖訪問,B 在訪問前嘗試加鎖,拿不到鎖,B 阻塞。C 線程不去加鎖,而直接訪問 該全局變量,依然能夠訪問,但會出現數據混亂。
所以,互斥鎖實質上是操作系統提供的一把“建議鎖”(又稱“協同鎖”),建議程序中有多線程訪問共享資源 的時候使用該機制。但,并沒有強制限定。
因此,即使有了 mutex,如果有線程不按規則來訪問數據,依然會造成數據混亂。
主要應用函數:
pthread_mutex_init 函數
初始化一個互斥鎖(互斥量)—> 初值可看作 1 int pthread_mutex_init (pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
參 1:傳出參數,調用時應傳 &mutex
restrict 關鍵字:只用于限制指針,告訴編譯器,所有修改該指針指向內存中內容的操作,只能通過本指針完成。 不能通過除本指針以外的其他變量或指針修改
參 2:互斥量屬性。是一個傳入參數,通常傳 NULL,選用默認屬性(線程間共享)。 參 APUE.12.4 同步屬性
pthread_mutex_destroy 函數
銷毀一個互斥鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);pthread_mutex_lock 函數
加鎖成功。可理解為將 mutex–(或-1)
int pthread_mutex_lock(pthread_mutex_t *mutex);pthread_mutex_unlock 函數
解鎖成功。可理解為將 mutex++(或+1)
int pthread_mutex_unlock(pthread_mutex_t *mutex);pthread_mutex_trylock 函數
嘗試加鎖
int pthread_mutex_trylock(pthread_mutex_t *mutex);示例:子線程打印小寫,主控線程打印大寫
#include<stdio.h> #include<unistd.h> #include<pthread.h> #include<stdlib.h> #include<string.h>pthread_mutex_t mutex; //鎖的定義void *tfn(void *arg) {srand(time(NULL));while(1){pthread_mutex_lock(&mutex);printf("hello");sleep(rand() % 3); /*模擬長時間操作共享資源,導致cpu易主,產生與時間>有關的錯誤*/printf("world\n");pthread_mutex_unlock(&mutex);sleep(rand() % 3); } return NULL; }int main(void) {pthread_t tid;srand(time(NULL));pthread_mutex_init(&mutex,NULL); //mutex == 1pthread_create(&tid,NULL,tfn,NULL);while(1){pthread_mutex_lock(&mutex);printf("HELLO");sleep(rand()%3);printf("WORLD\n");pthread_mutex_unlock(&mutex); sleep(rand() % 3);}pthread_mutex_destroy(&mutex);}線程在操作完共享資源后本應該立即解鎖,但修改后,線程抱著鎖睡眠。睡醒解鎖后又立即加鎖,這兩個 庫函數本身不會阻塞。
所以在這兩行代碼之間失去 cpu 的概率很小。因此,另外一個線程很難得到加鎖的機會
注意事項:在訪問共享資源前加鎖,訪問結束后立即解鎖。鎖的“粒度”應越小越好。
死鎖
讀寫鎖
與互斥量類似,但讀寫鎖允許更高的并行性。其特性為:寫獨占,讀共享。
讀寫鎖狀態
一把讀寫鎖具備三種狀態:
讀寫鎖特性:
主要應用函數:
pthread_rwlock_init 函數
初始化一把讀寫鎖
int pthread_rwlock_init(pthread_rwlock_t *restrictrwlock,const pthread_rwlockattr_t *restrictattr);
參 2:attr 表讀寫鎖屬性,通常使用默認屬性,傳 NULL 即可。
pthread_rwlock_destroy 函數
銷毀一把讀寫鎖
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);pthread_rwlock_rdlock 函數
以讀方式請求讀寫鎖。(常簡稱為:請求讀鎖)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);pthread_rwlock_wrlock 函數
以寫方式請求讀寫鎖。(常簡稱為:請求寫鎖)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);pthread_rwlock_unlock 函數
解鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);pthread_rwlock_tryrdlock 函數
非阻塞以讀方式請求讀寫鎖(非阻塞請求讀鎖)
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);pthread_rwlock_trywrlock 函數
非阻塞以寫方式請求讀寫鎖(非阻塞請求寫鎖)
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);同時有多個線程對同一全局數據讀、寫操作
#include<stdio.h> #include<unistd.h> #include<pthread.h>int counter; //全局資源pthread_rwlock_t rwlock;void *th_write(void *arg) //寫線程 {int t;int i = (int)arg;while(1){t = counter;usleep(1000);pthread_rwlock_wrlock(&rwlock);printf("========write %d: %lu: counter=%d ++counter=%d\n",i,pthread_self(),t,++counter);pthread_rwlock_unlock(&rwlock);usleep(5000);} return NULL; }void *th_read(void *arg) {int i = (int)arg;while(1){pthread_rwlock_rdlock(&rwlock);printf("---------read %d: %lu: %d\n",i,pthread_self(),counter);pthread_rwlock_unlock(&rwlock);usleep(900);} return NULL; } int main(void) {int i;pthread_t tid[8];pthread_rwlock_init(&rwlock,NULL);for(i = 0; i < 3; i++)pthread_create(&tid[i],NULL,th_write,(void *)i);for(i = 0; i < 5; i++)pthread_create(&tid[i+3],NULL,th_read,(void *)i);//三個寫線程,5個讀線程for(i = 0; i < 8; i++)pthread_join(tid[i],NULL);pthread_rwlock_destroy(&rwlock); //釋放讀寫鎖return 0; }總結
以上是生活随笔為你收集整理的Linux系统编程----16(线程同步,互斥量 mutex,互斥锁的相关函数,死锁,读写锁)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dnf采矿大亨怎么快速升级
- 下一篇: 我是一棵小草剧情介绍