四十、Linux 執行緒——互斥鎖和讀寫鎖
阿新 • • 發佈:2019-01-06
40.1 互斥鎖
40.1.1 介紹
- 互斥鎖(mutex)是一種簡單的加鎖的方法來控制對共享資源的訪問。
- 在同一時刻只能有一個執行緒掌握某個互斥鎖,擁有上鎖狀態的執行緒能夠對共享資源進行訪問。
- 若其他執行緒希望上鎖一個已經被上了互斥鎖的資源,則該執行緒掛起,直到上鎖的執行緒釋放互斥鎖為止。
- 互斥鎖的資料型別
- pthread_mutex_t
4.1.2 互斥鎖的建立和銷燬
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, constpthread_mutex_attr_t *mutexattr); int pthread_mutex_destroy(pthread_mutex_t *mutex);
- 函式引數:
- mutex:互斥鎖
- mutexattr:互斥鎖建立方式
- PTHREAD_MUTEX_INITALIZER:建立快速互斥鎖
- PTHREAD_RECURSIVE_MUTEX_INITALIZER_NP:建立遞迴互斥鎖
- PTHREAD_ERRORCHECK_MUTEX_INITALIZER_NP:建立檢錯互斥鎖
- 返回值:成功,則返回 0,否則,返回錯誤編號
4.1.3 互斥鎖上鎖和解鎖
1 #include <pthread.h> 2 /** 上鎖,拿不到鎖阻塞 */ 3 int pthread_mutex_lock(pthread_mutex_t *mutex); 4 /** 上鎖,拿不到鎖返回出錯資訊 */ 5 int pthread_mutex_trylock(pthread_mutex_t *mutex); 6 /** 釋放鎖 */ 7 int pthread_mutex_unlock(pthread_mutex_t *mutex);
- 函式引數:
- mutex:互斥鎖
- 返回值:成功返回0,出錯返回出錯碼
4.1.4 例子:
對前一個銀行賬戶程式碼進行修改:
atm_account.h
1 #ifndef __ATM_ACCOUNT_H__ 2 #define __ATM_ACCOUNT_H__ 3 4 #include <math.h> 5 #include <malloc.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <pthread.h> 11 12 /** 賬戶資訊 */ 13 typedef struct { 14 int code; ///< 銀行賬戶的編碼 15 double balance; ///< 賬戶餘額 16 17 pthread_mutex_t mutex; ///< 定義一把互斥鎖,用來對多執行緒操作的銀行賬戶(共享資源)進行加鎖 18 }atm_Account; 19 20 /** 建立賬戶 */ 21 extern atm_Account *atm_account_Create(int code, double balance); 22 /** 銷燬賬戶 */ 23 extern void atm_account_Destroy(atm_Account *account); 24 /** 取款 */ 25 extern double atm_account_Withdraw(atm_Account *account, double amt); 26 /** 存款 */ 27 extern double atm_account_Desposit(atm_Account *account, double amt); 28 /** 檢視賬戶餘額 */ 29 extern double atm_account_BalanceGet(atm_Account *account); 30 31 #endif
atm_account.c
1 #include "atm_account.h" 2 3 /** 建立賬戶 */ 4 atm_Account *atm_account_Create(int code, double balance) 5 { 6 atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account)); 7 if(NULL == account) { 8 return NULL; 9 } 10 11 account->code = code; 12 account->balance = balance; 13 14 /** 對互斥鎖進行初始化 */ 15 pthread_mutex_init(&account->mutex, NULL); 16 17 return account; 18 } 19 20 /** 銷燬賬戶 */ 21 void atm_account_Destroy(atm_Account *account) 22 { 23 if(NULL == account){ 24 return ; 25 } 26 27 pthread_mutex_destroy(&account->mutex); 28 free(account); 29 } 30 31 /** 取款: 成功,則返回取款金額 */ 32 double atm_account_Withdraw(atm_Account *account, double amt) 33 { 34 if(NULL == account) { 35 return 0.0; 36 } 37 38 /** 對共享資源(賬戶進行加鎖) */ 39 pthread_mutex_lock(&account->mutex); 40 41 if(amt < 0 || amt > account->balance) { 42 pthread_mutex_unlock(&account->mutex); 43 return 0.0; 44 } 45 46 double balance_tmp = account->balance; 47 sleep(1); 48 balance_tmp -= amt; 49 account->balance = balance_tmp; 50 51 pthread_mutex_unlock(&account->mutex); 52 53 return amt; 54 } 55 56 /** 存款: 返回存款的金額 */ 57 double atm_account_Desposit(atm_Account *account, double amt) 58 { 59 if(NULL == account){ 60 return 0.0; 61 } 62 63 /** 對共享資源(賬戶進行加鎖) */ 64 pthread_mutex_lock(&account->mutex); 65 66 if(amt < 0){ 67 pthread_mutex_unlock(&account->mutex); 68 return 0.0; 69 } 70 71 double balance_tmp = account->balance; 72 sleep(1); 73 balance_tmp += amt; 74 account->balance = balance_tmp; 75 76 pthread_mutex_unlock(&account->mutex); 77 78 return amt; 79 } 80 81 /** 檢視賬戶餘額 */ 82 double atm_account_BalanceGet(atm_Account *account) 83 { 84 if(NULL == account){ 85 return 0.0; 86 } 87 88 /** 對共享資源(賬戶進行加鎖) */ 89 pthread_mutex_lock(&account->mutex); 90 91 double balance_tmp = account->balance; 92 pthread_mutex_unlock(&account->mutex); 93 94 return balance_tmp; 95 }
編譯執行結果如下:
不會發生同時取到 10000 的事情了。
40.2 互斥鎖的型別和屬性
40.2.1 互斥鎖屬性的建立和銷燬
1 #include <pthread.h> 2 int pthread_mutexattr_init(pthread_mutexattr_t *attr); 3 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
- 函式引數:
- attr:互斥鎖屬性
- 返回值:
- 成功返回0,出錯返回錯誤編號
40.2.2 互斥鎖程序共享屬性操作---獲得和設定
1 #include <pthread.h> 2 int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared); 3 int pthread_mutexattr_setpshared(const pthread_mutexattr_t *attr, int *pshared);
- 函式引數:
- attr:互斥鎖屬性
- pshared:程序共享屬性
- PTHREAD_PROCESS_PRIVATE(預設情況)
- 鎖只能用於一個程序內部的兩個執行緒進行互斥
- PTHREAD_PROCESS_SHARED
- 鎖可以用於兩個不同程序中的執行緒進行互斥
- PTHREAD_PROCESS_PRIVATE(預設情況)
- 返回值:成功返回0,出錯返回錯誤編號
40.2.3 互斥鎖型別操作
1 #include <pthread.h> 2 int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type); 3 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
- 函式引數:
- attr:互斥鎖屬性
- type:互斥鎖型別
- 標準互斥鎖:PTHREAD_MUTEX_NORMAL
- 第一次上鎖成功,第二次上鎖會阻塞
- 遞迴互斥鎖:PTHREAD_MUTEX_RECURSIVE
- 第一次上鎖成功,第二次以後上鎖還是成功,內部計數
- 檢錯互斥鎖:PTHREAD_MUTEX_ERRORCHECK
- 第一次上鎖成功,第二次上鎖會出錯
- 預設護持錯:PTHREAD_MUTEX_DEFAULT(同標準互斥鎖)
- 標準互斥鎖:PTHREAD_MUTEX_NORMAL
- 返回值:
- 成功返回 0, 出錯返回錯誤編號
40.2.4 例子
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <string.h> 5 6 7 int main(int argc, char *argv[]) 8 { 9 pthread_mutex_t mutex; 10 if(argc < 2){ 11 fprintf(stdout, "-usrage:%s [error | noraml | recursive\n]", argv[0]); 12 exit(1); 13 } 14 15 /** 定義互斥鎖屬性 */ 16 pthread_mutexattr_t mutexattr; 17 /** 初始化互斥鎖屬性 */ 18 pthread_mutexattr_init(&mutexattr); 19 20 if(!strcmp(argv[1], "error")){ 21 /** 設定互斥鎖型別 */ 22 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK); 23 } 24 else if(!strcmp(argv[1], "normal")){ 25 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL); 26 } 27 else if(!strcmp(argv[1], "recursive")){ 28 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); 29 } 30 31 pthread_mutex_init(&mutex, &mutexattr); 32 if(pthread_mutex_lock(&mutex) != 0){ 33 printf("lock failure\n"); 34 } 35 else{ 36 printf("lock success\n"); 37 } 38 39 if(pthread_mutex_lock(&mutex) != 0){ 40 printf("lock failure\n"); 41 } 42 else{ 43 printf("lock success\n"); 44 } 45 46 pthread_mutex_unlock(&mutex); 47 pthread_mutex_unlock(&mutex); 48 49 pthread_mutexattr_destroy(&mutexattr); 50 pthread_mutex_destroy(&mutex); 51 52 return 0; 53 }View Code
編譯執行:
40.3 讀寫鎖
40.3.1 讀寫鎖介紹
- 執行緒使用互斥鎖缺乏讀併發性
- 當讀操作較多,寫操作較少時,可用讀寫鎖提高執行緒讀併發性
- 讀寫鎖資料型別:pthread_rwlock_t
40.3.2 讀寫鎖建立和銷燬
1 #include <pthread.h> 2 int phtread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 3 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
- 函式引數:
- rwlock:讀寫鎖
- attr:讀寫鎖屬性
- 返回值:成功返回0,出錯返回錯誤編號
40.3.3 讀寫鎖的加鎖和解鎖
1 #include <pthread.h> 2 /** 加讀鎖 */ 3 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); 4 /** 加寫鎖 */ 5 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 6 /** 釋放鎖 */ 7 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
- 函式引數:rwlock:讀寫鎖
- 返回值:成功返回 0;出錯,返回錯誤編號
40.3.4 讀寫鎖特性例子
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <pthread.h> 4 #include <string.h> 5 6 /** 定義讀寫鎖 */ 7 pthread_rwlock_t rwlock; 8 9 10 int main(int argc, char *argv[]) 11 { 12 if(argc < 3){ 13 printf("-usage:%s [r | w] [r | w]\n", argv[0]); 14 exit(1); 15 } 16 17 /** 讀寫鎖初始化 */ 18 pthread_rwlock_init(&rwlock, NULL); 19 if(!strcmp("r", argv[1])){ 20 /** 加讀鎖 */ 21 if(pthread_rwlock_rdlock(&rwlock) != 0){ 22 printf("first read lock failure\n"); 23 } 24 else { 25 printf("first read lock success\n"); 26 } 27 } 28 else if(!strcmp("w", argv[1])){ 29 /** 加寫鎖 */ 30 if(pthread_rwlock_wrlock(&rwlock) != 0){ 31 printf("first write lock failure\n"); 32 } 33 else { 34 printf("firest write lock success\n"); 35 } 36 } 37 38 if(!strcmp("r", argv[2])){ 39 /** 加讀鎖 */ 40 if(pthread_rwlock_rdlock(&rwlock) != 0){ 41 printf("second read lock failure\n"); 42 } 43 else { 44 printf("second read lock success\n"); 45 } 46 } 47 else if(!strcmp("w", argv[2])){ 48 /** 加寫鎖 */ 49 if(pthread_rwlock_wrlock(&rwlock) != 0){ 50 printf("first write lock failure\n"); 51 } 52 else { 53 printf("firest write lock success\n"); 54 } 55 } 56 57 pthread_rwlock_unlock(&rwlock); 58 pthread_rwlock_unlock(&rwlock); 59 60 pthread_rwlock_destroy(&rwlock); 61 62 return 0; 63 }
編譯執行結果如下:
由執行結果可以知道,當都上讀鎖的時候,都可以上鎖成功;先上讀鎖,再上寫鎖,寫鎖會阻塞;先上寫鎖後,不管是上讀鎖還是上寫鎖,都會失敗。
- 讀寫鎖特性:
- 讀和讀:不排斥
- 讀和寫:排斥
- 寫和寫:排斥
40.3.5 銀行例子修改
atm_account.h
1 #ifndef __ATM_ACCOUNT_H__ 2 #define __ATM_ACCOUNT_H__ 3 4 #include <math.h> 5 #include <malloc.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <unistd.h> 10 #include <pthread.h> 11 12 /** 賬戶資訊 */ 13 typedef struct { 14 int code; ///< 銀行賬戶的編碼 15 double balance; ///< 賬戶餘額 16 17 /** 建議互斥鎖用來鎖定一個賬戶(共享資源),和賬戶繫結再一起, 18 * 儘量不設定成全域性變數,否則可能出現一把鎖去鎖幾百個賬戶,導致併發效能降低 */ 19 //pthread_mutex_t mutex; ///< 定義一把互斥鎖,用來對多執行緒操作的銀行賬戶(共享資源)進行加鎖 20 21 pthread_rwlock_t rwlock; ///<定義讀寫鎖 22 }atm_Account; 23 24 /** 建立賬戶 */ 25 extern atm_Account *atm_account_Create(int code, double balance); 26 /** 銷燬賬戶 */ 27 extern void atm_account_Destroy(atm_Account *account); 28 /** 取款 */ 29 extern double atm_account_Withdraw(atm_Account *account, double amt); 30 /** 存款 */ 31 extern double atm_account_Desposit(atm_Account *account, double amt); 32 /** 檢視賬戶餘額 */ 33 extern double atm_account_BalanceGet(atm_Account *account); 34 35 #endif
atm_count.c
1 #include "atm_account.h" 2 3 /** 建立賬戶 */ 4 atm_Account *atm_account_Create(int code, double balance) 5 { 6 atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account)); 7 if(NULL == account) { 8 return NULL; 9 } 10 11 account->code = code; 12 account->balance = balance; 13 14 /** 對互斥鎖進行初始化 */ 15 //pthread_mutex_init(&account->mutex, NULL); 16 17 /** 初始化讀寫鎖 */ 18 pthread_rwlock_init(&account->rwlock, NULL); 19 20 return account; 21 } 22 23 /** 銷燬賬戶 */ 24 void atm_account_Destroy(atm_Account *account) 25 { 26 if(NULL == account){ 27 return ; 28 } 29 30 //pthread_mutex_destroy(&account->mutex); 31 pthread_rwlock_destroy(&account->rwlock); ///< 銷燬讀寫鎖 32 free(account); 33 } 34 35 /** 取款: 成功,則返回取款金額 */ 36 double atm_account_Withdraw(atm_Account *account, double amt) 37 { 38 if(NULL == account) { 39 return 0.0; 40 } 41 42 /** 對共享資源(賬戶進行加鎖) */ 43 //pthread_mutex_lock(&account->mutex); 44 pthread_rwlock_wrlock(&account->rwlock); ///< 加寫鎖 45 46 if(amt < 0 || amt > account->balance) { 47 //pthread_mutex_unlock(&account->mutex); 48 pthread_rwlock_unlock(&account->rwlock); ///< 釋放寫鎖 49 return 0.0; 50 } 51 52 double balance_tmp = account->balance; 53 sleep(1); 54 balance_tmp -= amt; 55 account->balance = balance_tmp; 56 57 //pthread_mutex_unlock(&account->mutex); 58 pthread_rwlock_unlock(&account->rwlock); ///< 釋放寫鎖 59 return amt; 60 } 61 62 /** 存款: 返回存款的金額 */ 63 double atm_account_Desposit(atm_Account *account, double amt) 64 { 65 if(NULL == account){ 66 return 0.0; 67 } 68 69 /** 對共享資源(賬戶進行加鎖) */ 70 //pthread_mutex_lock(&account->mutex); 71 pthread_rwlock_wrlock(&account->rwlock); ///< 加寫鎖 72 73 if(amt < 0){ 74 //pthread_mutex_unlock(&account->mutex); 75 pthread_rwlock_unlock(&account->rwlock); ///< 釋放寫鎖 76 return 0.0; 77 } 78 79 double balance_tmp = account->balance; 80 sleep(1); 81 balance_tmp += amt; 82 account->balance = balance_tmp; 83 84 //pthread_mutex_unlock(&account->mutex); 85 pthread_rwlock_unlock(&account->rwlock); ///< 釋放寫鎖 86 return amt; 87 } 88 89 /** 檢視賬戶餘額 */ 90 double atm_account_BalanceGet(atm_Account *account) 91 { 92 if(NULL == account){ 93 return 0.0; 94 } 95 96 /** 對共享資源(賬戶進行加鎖) */ 97 //pthread_mutex_lock(&account->mutex); 98 pthread_rwlock_rdlock(&account->rwlock); ///< 上讀鎖 99 100 double balance_tmp = account->balance; 101 //pthread_mutex_unlock(&account->mutex); 102 pthread_rwlock_unlock(&account->rwlock); ///< 釋放寫鎖 103 104 return balance_tmp; 105 }
編譯執行: