【线程】读写锁

一、概念

一把读写锁具备三种状态:

  • 读模式下加锁状态(读锁)
  • 写模式下加锁转态(写锁)
  • 不加锁状态

2. 读写锁特性:

  • 读写锁是写模式加锁时,解锁前,所有对该锁加锁的线程都会阻塞。
  • 读写锁是读模式加时,如果线程以读模式加锁会成功,如果线程以写模式加锁会阻塞
  • 读写锁是读模式加锁时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程,那么读模式会读阻塞随后的读模式锁请求,优先满足写模式锁,读锁、写锁并行阻塞,写锁优先级高

读写锁也叫共享_独占锁,当读写锁以读模式锁住时,它以共享模式锁住;当它以写模式锁住时,它是独占锁住的,写独占,读共享


3. 读写锁的特性:

线程A加读锁成功, 又来了三个线程, 做读操作, 可以加锁成功

  • 读共享 - 并行处理

线程A加写锁成功, 又来了三个线程, 做读操作, 三个线程阻塞

  • 写独占

线程A加读锁成功, 又来了B线程加写锁阻塞, 又来了C线程加读锁阻塞

  • 读写不能同时
  • 写的优先级高

4. 读写锁场景:

线程A加写锁成功, 线程B请求读锁

  • 线程B阻塞

线程A持有读锁, 线程B请求写锁

  • 线程B阻塞

线程A拥有读锁, 线程B请求读锁

  • 线程B加锁成功

线程A持有读锁, 然后线程B请求写锁, 然后线程C请求读锁

  • B阻塞,c阻塞 - 写的优先级高
  • A解锁,B线程加写锁成功,C继续阻塞
  • B解锁,C加读锁成功

线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁

  • BC阻塞
  • A解锁,C加写锁成功,B继续阻塞
  • C解锁,B加读锁成功

二、主要函数

1. 函数原型:

#include <pthread.h>
pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);  初始化读写锁
pthread_rwlock_destroy(pthread_rwlock_t *rwlock);	 销毁读写锁

2. 函数原型:阻塞版本

#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);  解锁

 

三、程序清单

1. 测试代码:

#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 t;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()
{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);for (i = 0; i < 8; ++i)pthread_join(tid[i], NULL);pthread_rwlock_destroy(&rwlock);return 0;
}

输出结果:

 

互斥锁实现读写锁
 

#include<pthread.h>pthread_mutex_t rdLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t wrLock = PTHREAD_MUTEX_INITIALIZER;
int readCnt = 0;//设置读锁调用次数的标志位//实现读锁(共享锁)
void rdLock() 
{pthread_mutex_lock(&rdLock);readCnt++;if (readCnt == 1)//有人读,于是阻塞写锁pthread_mutex_lock(&wrLock);pthread_mutex_unlock(&rdLock);
}void rdUnlock() 
{pthread_mutex_lock(&rdLock);readCnt--;if (readCnt == 0)//表示已没有人在读,释放写锁,可以写入了pthread_mutex_unlock(&wrLock);pthread_mutex_unlock(&rdLock);
}void wrLock() 
{pthread_mutex_lock(&wrLcok);
}void wrUnlock() 
{pthread_mutex_unlock(&wrLock);
}

一、实验项目

问题描述】程序 trainticket 中,有 100 个线程,其中 90 个线程是查余票数量的,只有 10 个线程抢票,每个线程一次买 10 张票。初始状态下一共有 1000 张票。因此执行完毕后,还会剩下 900 张票。

程序 trainticket 在运行的时候需要传入参数,即:

  • 参数 0:表示不加任何锁
  • 参数 1:表示使用读写锁
  • 参数 2:表示使用互斥量
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>struct Ticket 
{int remain;              // 余票数,初始化为 1000pthread_rwlock_t rwlock; // 读写锁pthread_mutex_t mlock;   // 互斥锁,主要是为了和读写锁进行对比
}ticket;// 通过命令行传参数来取得这个值,用来控制到底使用哪一种锁
// 0:不加锁 1:加读写锁 2:加互斥锁
int lock = 0;void* query(void* arg)  //查票线程
{int name = (int)arg;sleep(rand() % 5 + 1);if (lock == 1)pthread_rwlock_rdlock(&ticket.rwlock); // 读模式加锁else if (lock == 2)pthread_mutex_lock(&ticket.mlock);int remain = ticket.remain;sleep(1);printf("%03d query: %d\n", name, remain);if (lock == 1)pthread_rwlock_unlock(&ticket.rwlock);else if (lock == 2)pthread_mutex_unlock(&ticket.mlock);return NULL;
}void* buy(void* arg)  // 抢票线程
{int name = (int)arg;if (lock == 1)pthread_rwlock_wrlock(&ticket.rwlock); // 写模式加锁else if (lock == 2)pthread_mutex_lock(&ticket.mlock);int remain = ticket.remain;remain -= 10; // 一次买 10 张票sleep(1);ticket.remain = remain;printf("%03d buy 10 tickets\n", name);if (lock == 1)pthread_rwlock_unlock(&ticket.rwlock);else if (lock == 2)pthread_mutex_unlock(&ticket.mlock);sleep(rand() % 5 + 2);return NULL;
}int main(int argc, char* argv[]) 
{lock = 0;if (argc >= 2) lock = atoi(argv[1]);int names[100];pthread_t tid[100];int i;for (i = 0; i < 100; ++i) names[i] = i;ticket.remain = 1000;printf("remain ticket = %d\n", ticket.remain);pthread_rwlock_init(&ticket.rwlock, NULL);pthread_mutex_init(&ticket.mlock, NULL);for (i = 0; i < 100; ++i) {if (i % 10 == 0)pthread_create(&tid[i], NULL, buy, (void*)names[i]);elsepthread_create(&tid[i], NULL, query, (void*)names[i]);}for (i = 0; i < 100; ++i) pthread_join(tid[i], NULL);pthread_rwlock_destroy(&ticket.rwlock);pthread_mutex_destroy(&ticket.mlock);printf("remain ticket = %d\n", ticket.remain);return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不愧是阿里大佬,mysql存储过程写法案例

容器化时代来了 虚拟化技术已经走过了三个时代&#xff0c;没有容器化技术的演进就不会有 Docker 技术的诞生。 虚拟化技术演进 &#xff08;1&#xff09;物理机时代&#xff1a;多个应用程序可能会跑在一台机器上。 &#xff08;2&#xff09;虚拟机时代&#xff1a;一台物…

不看绝对血亏!java字符串转json

一、对Kafka的认识 1.Kafka的基本概念 2.安装与配置 3.生产与消费 4.服务端参数配置 二、生产者 1.客户端开发 必要的参数配置消息的发送序列化分区器生产者拦截器 2.原理分析 整体架构元数据的更新 3.重要的生产者参数 三、消费者 1.消费者与消费组 2.客户端开发 必要的…

【线程】屏障

一、主要函数应用 1. 函数原型&#xff1a;初始化和回收 int pthread_barrier_init(pthread *barrier, const pthread_barrier_t *attr, unsigned int count); int pthread_barrier_destroy(pthread_barrier_t *barrier);分析&#xff1a; 初始化屏障时&#xff0c;可以使用c…

中高级工程师Java开发!java生成随机数代码包

微服务是什么 微服务起源于2005年Peter Rodgers博士在云端运算博览会提出的微Web服务(Micro-Web-Service)&#xff0c;根本思想类似于Unix的管道设计理念。2014年&#xff0c;由Martin Fowler 与 James Lewis共同提出了微服务的概念&#xff0c;定义了微服务架构风格是一种通过…

Java软件开发面试题!hr和技术经理一起面

springspring5最全知识梳理大纲&#xff08;总&#xff09; spring最全知识梳理大纲&#xff08;总&#xff09; spring5最全知识梳理大纲&#xff08;总&#xff09; spring最全知识梳理大纲&#xff08;分&#xff09; 为什么要使用Spring&#xff1f; Bean spring事务 面向…

Java进阶之光!javaunicode码转字符

01 如何理解高并发&#xff1f; 高并发意味着大流量&#xff0c;需要运用技术手段抵抗流量的冲击&#xff0c;这些手段好比操作流量&#xff0c;能让流量更平稳地被系统所处理&#xff0c;带给用户更好的体验。 我们常见的高并发场景有&#xff1a;淘宝的双11、春运时的抢票、…

Java进阶之光!java字符串类型转换为int

阿里巴巴篇 1.扎实的计算机专业基础&#xff0c;包括算法和数据结构&#xff0c;操作系统&#xff0c;计算机网络&#xff0c;计算机体系结构&#xff0c;数据库等2.具有扎实的Java编程基础&#xff0c;理解IO、多线程等基础框架3.熟练使用Linux系统的常用命令及shell有一定了…

Java进阶之光!mysql创建用户并授权建表

正文 MyBatis 的整体架构分为三层&#xff0c; 分别是基础支持层、核心处理层和接口层&#xff0c;如下图所示。 基础支持层 反射模块 该模块对 Java 原生的反射进行了良好的封装&#xff0c;提供了更加简洁易用的 API &#xff0c;方便上层使调用&#xff0c;并且对反射操作…

Java进阶之光!mysql安装包安装教程

我听到的一些发声 你们赚的钱已经可以了&#xff1a; 我一个发小是做土木工程的&#xff0c;上海大学博士&#xff0c;参与很多著名建筑的工程&#xff0c;但是从薪资上看&#xff0c;还不如一些稍微像样的公司的6年多的高级开发。为什么&#xff1f;这就是行业的红利&#xf…

Java进阶学习资料!dockerjar内存

准备好套路 **①自我介绍&#xff1a;**千万不能筐瓢&#xff0c;一定要牢记&#xff0c;自然流畅地介绍自己的学习经历、工作经历、项目经历、个人优势等等&#xff1b; **②抽象概念&#xff1a;**当面试官问你是如何理解多线程的时候&#xff0c;你要知道从定义、来源、实…

管道(二)

无名管道 测试代码&#xff1a; #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <string.h>#define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) #define TEST_SIZE 68*1024int mai…

Java进阶:java字符串定位语句

正文 模块&#xff08;Module&#xff09;、组件&#xff08;Component&#xff09;、包&#xff08;Package&#xff09;&#xff0c;这些概念对于我们技术同学并不陌生&#xff0c;但并不是所有人都能理解其要义。 深入理解之后&#xff0c;我才发现&#xff0c;其背后的深…

有名管道

一、有名管道 1. 为何提出有名管道的说法&#xff0c;目的是为了克服无名管道的不足之处&#xff1a; 无名管道只能是用于具有亲缘关系的进程之间&#xff0c;这就限制了无名管道的使用范围。有名管道可以使互不相关的两个进程互相通信&#xff0c;有名管道可以通过路径名来指…

Java进阶:mysql的事务隔离级别面试题

一面&#xff1a; 阿里巴巴面试答案文末可以领取&#xff01; 1. 触发新生代GC&#xff0c;如果存活对象总量大于survivor区容量&#xff0c;咋办 2. 如果任务很多&#xff0c;线程池的阻塞队列会撑爆内存的哪个区域 3. 栈在堆上吗 4. GC root有哪些 5. 实例变量可以是GC…

Java面试2021,java数据可视化项目

AOP简介 AOP (Aspect Oriented Programing) 称为&#xff1a;面向切面编程&#xff0c;它是一种编程思想。AOP 是 OOP&#xff08;面向对象编程 Object Oriented Programming)的思想延续 AOP采取横向抽取机制&#xff0c;取代了传统纵向继承体系重复性代码的编写方式&#xff0…

gcc的使用

一、gcc编译过程示意图 分析&#xff1a; hello程序是一个高级&#xff23;语言程序&#xff0c;这种形式容易被人读懂。为了在系统上运行hello.c程序&#xff0c;每条&#xff23;语句都必须转化为低级机器指令。然后将这些指令打包成可执行目标文件格式&#xff0c;并以二进…

Java面试2021,java黑马百度云

线程是否要锁住同步资源 锁住 悲观锁不锁住 乐观锁 锁住同步资源失败 线程是否要阻塞 阻塞不阻塞自旋锁&#xff0c;适应性自旋锁 多个线程竞争同步资源的流程细节有没有区别 不锁住资源&#xff0c;多个线程只有一个能修改资源成功&#xff0c;其它线程会重试无锁同一个线…

oppoJava面试题,java声明全局变量的关键字

蚂蚁一面 ⼀⾯就做了⼀道算法题&#xff0c;要求两⼩时内完成&#xff0c;给了⻓度为N的有重复元素的数组&#xff0c;要求输出第10⼤的数。典型的TopK问题&#xff0c;快排算法搞定。算法题要注意的是合法性校验、边界条件以及异常的处理。另外&#xff0c;如果要写测试⽤例&…

System V 消息队列

一、System V 消息队列 有一个队列&#xff0c;队列存放各种消息。每个进程可以把数据封存在消息中&#xff0c;再放入队列。每个进程都可以拿到消息队列&#xff0c;再从中取出/放入消息。 消息队列也有管道一样的不足&#xff0c;就是每个消息的最大长度是有上限的&#xf…

oppoJava面试题,java连接数据库详细步骤

美团一面&#xff08;50分钟左右&#xff09; 进程和线程死锁的必要条件网络&#xff0c;七层协议TCP和UDP的区别hashmap 详细讲一下hashmap底层是如何解决hash冲突的hashmap和linkedhashmap数据库的索引&#xff0c;为什么推荐自增id&#xff0c;有什么优点MySQL的引擎&#…