四十、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 互斥锁上锁和解锁
#include <pthread.h>
/** 上锁,拿不到锁阻塞 */
int pthread_mutex_lock(pthread_mutex_t *mutex);
/** 上锁,拿不到锁返回出错信息 */
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/** 释放锁 */
int pthread_mutex_unlock(pthread_mutex_t *mutex);
- 函数参数:
- mutex:互斥锁
- 返回值:成功返回0,出错返回出错码
4.1.4 例子:
对前一个银行账户代码进行修改:
atm_account.h
#ifndef __ATM_ACCOUNT_H__
#define __ATM_ACCOUNT_H__ #include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
10 #include <pthread.h> /** 账户信息 */
typedef struct {
int code; ///< 银行账户的编码
double balance; ///< 账户余额 17 pthread_mutex_t mutex; ///< 定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁
}atm_Account; /** 创建账户 */
extern atm_Account *atm_account_Create(int code, double balance);
/** 销毁账户 */
extern void atm_account_Destroy(atm_Account *account);
/** 取款 */
extern double atm_account_Withdraw(atm_Account *account, double amt);
/** 存款 */
extern double atm_account_Desposit(atm_Account *account, double amt);
/** 查看账户余额 */
extern double atm_account_BalanceGet(atm_Account *account); #endif
atm_account.c
#include "atm_account.h" /** 创建账户 */
atm_Account *atm_account_Create(int code, double balance)
{
atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account));
if(NULL == account) {
return NULL;
} account->code = code;
account->balance = balance; /** 对互斥锁进行初始化 */
15 pthread_mutex_init(&account->mutex, NULL);
return account;
} /** 销毁账户 */
void atm_account_Destroy(atm_Account *account)
{
if(NULL == account){
return ;
} 27 pthread_mutex_destroy(&account->mutex);
free(account);
} /** 取款: 成功,则返回取款金额 */
double atm_account_Withdraw(atm_Account *account, double amt)
{
if(NULL == account) {
return 0.0;
} /** 对共享资源(账户进行加锁) */
39 pthread_mutex_lock(&account->mutex);
if(amt < || amt > account->balance) {
42 pthread_mutex_unlock(&account->mutex);
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp -= amt;
account->balance = balance_tmp; 51 pthread_mutex_unlock(&account->mutex);
return amt;
} /** 存款: 返回存款的金额 */
double atm_account_Desposit(atm_Account *account, double amt)
{
if(NULL == account){
return 0.0;
} /** 对共享资源(账户进行加锁) */
64 pthread_mutex_lock(&account->mutex);
if(amt < ){
67 pthread_mutex_unlock(&account->mutex);
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp += amt;
account->balance = balance_tmp; 76 pthread_mutex_unlock(&account->mutex);
return amt;
} /** 查看账户余额 */
double atm_account_BalanceGet(atm_Account *account)
{
if(NULL == account){
return 0.0;
} /** 对共享资源(账户进行加锁) */
89 pthread_mutex_lock(&account->mutex);
double balance_tmp = account->balance;
92 pthread_mutex_unlock(&account->mutex);
return balance_tmp;
}
编译运行结果如下:

不会发生同时取到 10000 的事情了。
40.2 互斥锁的类型和属性
40.2.1 互斥锁属性的创建和销毁
#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
- 函数参数:
- attr:互斥锁属性
- 返回值:
- 成功返回0,出错返回错误编号
40.2.2 互斥锁进程共享属性操作---获得和设置
#include <pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);
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 互斥锁类型操作
#include <pthread.h>
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type);
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 例子
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h> int main(int argc, char *argv[])
{
pthread_mutex_t mutex;
if(argc < ){
fprintf(stdout, "-usrage:%s [error | noraml | recursive\n]", argv[]);
exit();
} /** 定义互斥锁属性 */
pthread_mutexattr_t mutexattr;
/** 初始化互斥锁属性 */
pthread_mutexattr_init(&mutexattr); if(!strcmp(argv[], "error")){
/** 设置互斥锁类型 */
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
}
else if(!strcmp(argv[], "normal")){
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL);
}
else if(!strcmp(argv[], "recursive")){
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
} pthread_mutex_init(&mutex, &mutexattr);
if(pthread_mutex_lock(&mutex) != ){
printf("lock failure\n");
}
else{
printf("lock success\n");
} if(pthread_mutex_lock(&mutex) != ){
printf("lock failure\n");
}
else{
printf("lock success\n");
} pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex); pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(&mutex); return ;
}
编译运行:

40.3 读写锁
40.3.1 读写锁介绍
- 线程使用互斥锁缺乏读并发性
- 当读操作较多,写操作较少时,可用读写锁提高线程读并发性
- 读写锁数据类型:pthread_rwlock_t
40.3.2 读写锁创建和销毁
#include <pthread.h>
int phtread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
- 函数参数:
- rwlock:读写锁
- attr:读写锁属性
- 返回值:成功返回0,出错返回错误编号
40.3.3 读写锁的加锁和解锁
#include <pthread.h>
/** 加读锁 */
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
/** 加写锁 */
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
/** 释放锁 */
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
- 函数参数:rwlock:读写锁
- 返回值:成功返回 0;出错,返回错误编号
40.3.4 读写锁特性例子
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h> /** 定义读写锁 */
pthread_rwlock_t rwlock; int main(int argc, char *argv[])
{
if(argc < ){
printf("-usage:%s [r | w] [r | w]\n", argv[]);
exit();
} /** 读写锁初始化 */
pthread_rwlock_init(&rwlock, NULL);
if(!strcmp("r", argv[])){
/** 加读锁 */
if(pthread_rwlock_rdlock(&rwlock) != ){
printf("first read lock failure\n");
}
else {
printf("first read lock success\n");
}
}
else if(!strcmp("w", argv[])){
/** 加写锁 */
if(pthread_rwlock_wrlock(&rwlock) != ){
printf("first write lock failure\n");
}
else {
printf("firest write lock success\n");
}
} if(!strcmp("r", argv[])){
/** 加读锁 */
if(pthread_rwlock_rdlock(&rwlock) != ){
printf("second read lock failure\n");
}
else {
printf("second read lock success\n");
}
}
else if(!strcmp("w", argv[])){
/** 加写锁 */
if(pthread_rwlock_wrlock(&rwlock) != ){
printf("first write lock failure\n");
}
else {
printf("firest write lock success\n");
}
} pthread_rwlock_unlock(&rwlock);
pthread_rwlock_unlock(&rwlock); pthread_rwlock_destroy(&rwlock); return ;
}
编译运行结果如下:

由运行结果可以知道,当都上读锁的时候,都可以上锁成功;先上读锁,再上写锁,写锁会阻塞;先上写锁后,不管是上读锁还是上写锁,都会失败。
- 读写锁特性:
- 读和读:不排斥
- 读和写:排斥
- 写和写:排斥
40.3.5 银行例子修改
atm_account.h
#ifndef __ATM_ACCOUNT_H__
#define __ATM_ACCOUNT_H__ #include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h> /** 账户信息 */
typedef struct {
int code; ///< 银行账户的编码
double balance; ///< 账户余额 17 /** 建议互斥锁用来锁定一个账户(共享资源),和账户绑定再一起,
18 * 尽量不设置成全局变量,否则可能出现一把锁去锁几百个账户,导致并发性能降低 */
19 //pthread_mutex_t mutex; ///< 定义一把互斥锁,用来对多线程操作的银行账户(共享资源)进行加锁
20
21 pthread_rwlock_t rwlock; ///<定义读写锁
}atm_Account; /** 创建账户 */
extern atm_Account *atm_account_Create(int code, double balance);
/** 销毁账户 */
extern void atm_account_Destroy(atm_Account *account);
/** 取款 */
extern double atm_account_Withdraw(atm_Account *account, double amt);
/** 存款 */
extern double atm_account_Desposit(atm_Account *account, double amt);
/** 查看账户余额 */
extern double atm_account_BalanceGet(atm_Account *account); #endif
atm_count.c
#include "atm_account.h" /** 创建账户 */
atm_Account *atm_account_Create(int code, double balance)
{
atm_Account *account = (atm_Account *)malloc(sizeof(atm_Account));
if(NULL == account) {
return NULL;
} account->code = code;
account->balance = balance; /** 对互斥锁进行初始化 */
15 //pthread_mutex_init(&account->mutex, NULL);
16
17 /** 初始化读写锁 */
18 pthread_rwlock_init(&account->rwlock, NULL);
return account;
} /** 销毁账户 */
void atm_account_Destroy(atm_Account *account)
{
if(NULL == account){
return ;
} 30 //pthread_mutex_destroy(&account->mutex);
31 pthread_rwlock_destroy(&account->rwlock); ///< 销毁读写锁
free(account);
} /** 取款: 成功,则返回取款金额 */
double atm_account_Withdraw(atm_Account *account, double amt)
{
if(NULL == account) {
return 0.0;
} /** 对共享资源(账户进行加锁) */
43 //pthread_mutex_lock(&account->mutex);
44 pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁
if(amt < || amt > account->balance) {
47 //pthread_mutex_unlock(&account->mutex);
48 pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp -= amt;
account->balance = balance_tmp; 57 //pthread_mutex_unlock(&account->mutex);
58 pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
return amt;
} /** 存款: 返回存款的金额 */
double atm_account_Desposit(atm_Account *account, double amt)
{
if(NULL == account){
return 0.0;
} /** 对共享资源(账户进行加锁) */
70 //pthread_mutex_lock(&account->mutex);
71 pthread_rwlock_wrlock(&account->rwlock); ///< 加写锁
if(amt < ){
74 //pthread_mutex_unlock(&account->mutex);
75 pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
return 0.0;
} double balance_tmp = account->balance;
sleep();
balance_tmp += amt;
account->balance = balance_tmp; 84 //pthread_mutex_unlock(&account->mutex);
85 pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
return amt;
} /** 查看账户余额 */
double atm_account_BalanceGet(atm_Account *account)
{
if(NULL == account){
return 0.0;
} /** 对共享资源(账户进行加锁) */
97 //pthread_mutex_lock(&account->mutex);
98 pthread_rwlock_rdlock(&account->rwlock); ///< 上读锁
double balance_tmp = account->balance;
101 //pthread_mutex_unlock(&account->mutex);
102 pthread_rwlock_unlock(&account->rwlock); ///< 释放写锁
return balance_tmp;
}
编译运行:

四十、Linux 线程——互斥锁和读写锁的更多相关文章
- golang互斥锁和读写锁
一.互斥锁 互斥锁是传统的并发程序对共享资源进行访问控制的主要手段.它由标准库代码包sync中的Mutex结构体类型代表.sync.Mutex类型(确切地说,是*sync.Mutex类型)只有两个公开 ...
- PHP程序中的文件锁、互斥锁、读写锁使用技巧解析
文件锁全名叫 advisory file lock, 书中有提及. 这类锁比较常见,例如 mysql, php-fpm 启动之后都会有一个pid文件记录了进程id,这个文件就是文件锁. 这个锁可以防止 ...
- golang 互斥锁和读写锁
golang 互斥锁和读写锁 golang中sync包实现了两种锁Mutex(互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. ty ...
- Go语言基础之13--线程安全及互斥锁和读写锁
一.线程安全介绍 1.1 现实例子 A. 多个goroutine同时操作一个资源,这个资源又叫临界区 B. 现实生活中的十字路口,通过红路灯实现线程安全 C. 火车上的厕所(进去之后先加锁,在上厕所, ...
- Java中的锁-悲观锁、乐观锁,公平锁、非公平锁,互斥锁、读写锁
总览图 如果文中内容有错误,欢迎指出,谢谢. 悲观锁.乐观锁 悲观锁.乐观锁使用场景是针对数据库操作来说的,是一种锁机制. 悲观锁(Pessimistic Lock):顾名思义,就是很悲观,每次去拿数 ...
- UNIX环境高级编程——线程同步之互斥锁、读写锁和条件变量(小结)
一.使用互斥锁 1.初始化互斥量 pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥量 int pthread_mutex_init( ...
- Linux线程-互斥锁pthread_mutex_t
在线程实际运行过程中,我们经常需要多个线程保持同步.这时可以用互斥锁来完成任务:互斥锁的使用过程中,主要有pthread_mutex_init,pthread_mutex_destory,pthrea ...
- 多线程编程之Apue3rd_Chapter11之互斥锁_读写锁_自旋锁
学习了apue3rd的第11章,主要讲的是多线程编程.因为线程共享进程的资源比如堆和全局变量,多线程编程最重要的是,使用各种锁进行线程同步. 线程编程首先要学习的三个函数如下: #include &l ...
- Go语言中的互斥锁和读写锁(Mutex和RWMutex)
目录 一.Mutex(互斥锁) 不加锁示例 加锁示例 二.RWMutex(读写锁) 并发读示例 并发读写示例 三.死锁场景 1.Lock/Unlock不是成对出现 2.锁被拷贝使用 3.循环等待 虽然 ...
随机推荐
- nginx.conf(centos7 1.14)主配置文件修改
#nginx1.14 centos7# For more information on configuration, see:# * Official English Documentation: h ...
- Xposed+JustTrustMe+Android
场景介绍:APP抓包 引出的知识点:ssl-pinning. ssl-pinning: apk在开发时就将服务端证书一块打包到客户端里.这样在HTTPS建立时与服务端返回的证书比对一致性,进而识别出中 ...
- Codeforces Round #516 (Div. 2)D. Labyrinth(BFS)
题目链接:http://codeforces.com/contest/1064/problem/D 题目大意:给你一个n*m的图,图中包含两种符号,'.'表示可以行走,'*'表示障碍物不能行走,规定最 ...
- 纪中2018暑假培训day5提高b组改题记录
因为今天省选组也做a组,以为今天a组会很难,就做了做b组.t1和t3强行暴力,好在有t2保底.t1和正解就差一点,然而考试时死活想不起来...... 今天改题可以少改一道了!ovo 救救孩子吧!t1T ...
- iconv: iconv_open(pToCharset, pFromCharset); 的附加参数//IGNORE
今天在转换一个文件时iconv() 老是返回 -1, 提示编码转换失败. 一共 30 多个文件, 原编码都是一样的,为什么有的转换会失败,返回 -1呢? 网上搜索了一下, 找到一个随加参数: //IG ...
- Jmeter接口自动化
基本思路: 在excel中维护测试用例,包括访问协议,服务器名或IP,路径,请求的方法,端口号,提交参数,测试结果等,使用CSV Data Set Config读取请求信息并写入测试结果,后期只要维护 ...
- RAP Mock.js语法规范
Mock.js 的语法规范包括两部分: 数据模板定义规范(Data Template Definition,DTD) 数据占位符定义规范(Data Placeholder Definition,DPD ...
- Python函数--装饰器进阶
开放封闭原则 1.对扩展是开放的 为什么要对扩展开放呢? 我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代码扩展.添加新功能. 2.对修改是封 ...
- ElasticSearch6.3.2------查询
进入Kibana的控制台:http://localhost:5601/app/kibana#/dev_tools/ 先放一些测试数据进去,不想一条一条,就用bulk 注意格式 正确格式: 解释:ES期 ...
- Gym 101911F “Tickets”
传送门 题意: 给你一个由六位数字组成的门票编码x,并定义F(x) = | 前三位加和 - 后三位加和|: 求出给定的门票编码 x 之前并且 F(i) < F(x) 的 i 的总个数. 题解: ...