1. 程式人生 > >四十、Linux 執行緒——互斥鎖和讀寫鎖

四十、Linux 執行緒——互斥鎖和讀寫鎖

40.1 互斥鎖

40.1.1 介紹

  • 互斥鎖(mutex)是一種簡單的加鎖的方法來控制對共享資源的訪問。
    • 在同一時刻只能有一個執行緒掌握某個互斥鎖,擁有上鎖狀態的執行緒能夠對共享資源進行訪問。
    • 若其他執行緒希望上鎖一個已經被上了互斥鎖的資源,則該執行緒掛起,直到上鎖的執行緒釋放互斥鎖為止。
  • 互斥鎖的資料型別
    • pthread_mutex_t

4.1.2 互斥鎖的建立和銷燬

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const
pthread_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
        • 鎖可以用於兩個不同程序中的執行緒進行互斥
  • 返回值:成功返回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(同標準互斥鎖)
  • 返回值:
    • 成功返回 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 }

  編譯執行: