• 四十、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
    ]", 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
    ");
    34     }
    35     else{
    36         printf("lock success
    ");
    37     }
    38 
    39     if(pthread_mutex_lock(&mutex) != 0){
    40         printf("lock failure
    ");
    41     }
    42     else{
    43         printf("lock success
    ");
    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]
    ", 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
    ");
    23         }
    24         else {
    25             printf("first read lock success
    ");
    26         }
    27     }
    28     else if(!strcmp("w", argv[1])){
    29         /** 加写锁 */
    30         if(pthread_rwlock_wrlock(&rwlock) != 0){
    31             printf("first write lock failure
    ");
    32         }
    33         else {
    34             printf("firest write lock success
    ");
    35         }
    36     }
    37 
    38     if(!strcmp("r", argv[2])){
    39         /** 加读锁 */
    40         if(pthread_rwlock_rdlock(&rwlock) != 0){
    41             printf("second read lock failure
    ");
    42         }
    43         else {
    44             printf("second read lock success
    ");
    45         }
    46     }
    47     else if(!strcmp("w", argv[2])){
    48         /** 加写锁 */
    49         if(pthread_rwlock_wrlock(&rwlock) != 0){
    50             printf("first write lock failure
    ");
    51         }
    52         else {
    53             printf("firest write lock success
    ");
    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 }

      编译运行:

      

  • 相关阅读:
    良心之作送你几个Xsheel使用小技巧
    面试问Redis集群,被虐的不行了......
    一文搞定Redis五大数据类型及应用场景
    写给大忙人的Redis主从复制,花费五分钟让你面试不尴尬
    Redis删除策略和逐出策略
    一文带你了解Redis持久化完整版本
    MySQL--创建计算字段
    MySQL语句与正则表达式
    SQLZOO练习二--SELECT from Nobel Tutorial
    SQLZOO练习(一)SELECT BASICS,SELECT form world
  • 原文地址:https://www.cnblogs.com/kele-dad/p/10222621.html
Copyright © 2020-2023  润新知