Linux学习——浅谈读写锁的使用

Linux学习——浅谈读写锁的使用一、读写锁是什么?读写锁其实还是一种锁,是给一段临界区代码加锁,但是此加锁是在进行写操作的时候才会互斥,而在进行读的时候是可以共享的进行访问临界区的。读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程。当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步,读写锁的使用规则:只要没有写模式下的加锁,任…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、读写锁是什么?

读写锁其实还是一种锁,是给一段临界区代码加锁,但是此加锁是在进行写操作的时候才会互斥,而在进行读的时候是可以共享的进行访问临界区的。
读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程。当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步,

读写锁的使用规则:

  1. 只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁;
  2. 只有读写锁处于不加锁状态时,才能进行写模式下的加锁;
  3. 读写锁也称为共享-独占(shared-exclusive)锁,当读写锁以读模式加锁时,它是以共享模式锁住,当以写模式加锁时,它是以独占模式锁住。读写锁非常适合读数据的频率远大于写数据的频率从的应用中。这样可以在任何时刻运行多个读线程并发的执行,给程序带来了更高的并发度。

ps:读写锁本质上是一种自旋锁

二、为什么需要读写锁?

有时候,在多线程中,有一些公共数据修改的机会比较少,而读的机会却是非常多的,此公共数据的操作基本都是读,如果每次操作都给此段代码加锁,太浪费时间了而且也很浪费资源,降低程序的效率,因为读操作不会修改数据,只是做一些查询,所以在读的时候不用给此段代码加锁,可以共享的访问,只有涉及到写的时候,互斥的访问就好了

三、读写锁的行为

读写之间是互斥的—–>读的时候写阻塞,写的时候读阻塞,而且读和写在竞争锁的时候,写会优先得到锁
在这里插入图片描述

四、自旋锁&挂起等待锁(互斥锁)?

1.自旋锁

自旋锁是在发生获取不到锁的时候,会直接等待,不会被CPU直接调度走,而是会一直等到获取到锁,因为此锁是一直的在等待,所以不会有调度的开销,故此锁的效率比挂起等待锁的效率高,但是此锁会因不停的查看锁的释放情况,故会浪费更多的CPU资源

2.挂起等待锁

挂起等待锁是当某线程在执行临界区的代码时,那其他线程只能挂起等待,此时这些线程会被CPU调度走,等到锁释放(即就是临界区的代码被之前的那个线程已经执行完毕),而且被CPU调度的线程只有被调度回来才可以执行临界区的代码
挂起等待锁是在发生获取不到锁的时候,他会被CPU调度走,去做别的事,但是会时不时的去查看锁有没有被释放
ps:线程想执行临界区的代码的条件:
(1)锁释放;
(2)被CPU调度回来

3.自旋锁的优缺点

优点:效率高,避免了线程之间调度的开销
缺点:浪费CPU资源

4.挂起等待锁的优缺点

优点:不会浪费CPU的资源,比较灵活
缺点:效率不高,很可能会使临界区的代码不被任何线程执行,因为可能会是线程被
CPU调度走了但是却没有被调度回来

和互斥量不同的是:互斥量会把试图进入已保护的临界区的线程都阻塞;然而读写锁会视当前进入临界区的线程和请求进入临界区的线程的属性来判断是否允许线程进入。

相对互斥量只有加锁和不加锁两种状态,读写锁有三种状态:读模式下的加锁,写模式下的加锁,不加锁。

五、读写锁是怎么实现?

1.一种交易场所(存放数据的地方):可以是变量、链表、数组或其他数据结构
2.两种角色:读操作和写操作
3.三种关系:
(1)读和读之间没有关系
(2) 写和写之间是互斥关系
(3)读和写之间是同步互斥关系

ps:同步—->读和写在同时竞争锁的时候,写会优先的得到锁
互斥—->读的时候写阻塞,写的时候读阻塞

相关函数
(1)pthread_rwlock_init()—->初始化函数

功能:初始化读写锁
头文件:#include<pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthred_rwlockattr_t *restrict attr);

参数说明:
rwlock:是要进行初始化的
attr:是rwlock的属性
ps:此参数一般不关注,可设为NULL

(2)pthread_rwlock_destroy—->销毁函数

功能:销毁初始化的锁
头文件:#include<pthread.h>

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

参数说明:
rwlock:是需要进行销毁的锁

(3)加锁和解锁

在进行读操作的时候加的锁:

pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);

在进行写操作的时候加的锁:

pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);

对读/写统一进行解锁:

pthread_rwlock_unlock(pthread_rwlock_t* rwlock);

六、代码实现读写锁

#include<stdio.h>
#include<unistd.h>
#include<malloc.h>
#include<stdlib.h>
#include<pthread.h>
pthread_rwlock_t rwlock;//声明读写锁
int count;
//写者线程的入口函数
void*route_write(void*arg)
{ 

int i=*(int*)arg;//i是写者线程的编号
free(arg);
while(1){ 

int t=count;
//加锁
pthread_rwlock_wrlock(&rwlock);
printf("route_write:%d,%#x,count=%d,++count=%d\n",i,\
pthread_self(),t,++count);
//解锁
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
//读者线程的入口函数
void*route_read(void*arg)
{ 

int i=*(int*)arg;//i是读者线程的编号
free(arg);
while(1){ 

//加锁
pthread_rwlock_rdlock(&rwlock);
printf("route_read:%d,%#x,count=%d\n",i,pthread_self(),count);
//解锁
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
int main()
{ 

int i=0;
//初始化读写锁
pthread_rwlock_init(&rwlock,NULL);
pthread_t tid[8];
//创建3个写者线程
for(i=0;i<3;i++){ 

int*p=(int*)malloc(sizeof(int));
*p=i;
pthread_create(&tid[i],NULL,route_write,(void*)p);
}
//创建3个读者线程
for(i=0;i<5;i++){ 

int*p=(int*)malloc(sizeof(int));
*p=i;
pthread_create(&tid[i+3],NULL,route_read,(void*)p);
}
//主线程等待新创建的线程
for(i=0;i<8;i++)
pthread_join(tid[i],NULL);
//销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/169988.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Hadoop、Yarn和vcpu资源的配置

    Hadoop、Yarn和vcpu资源的配置

  • opengrok使用教程之单工程(上)

    opengrok使用教程之单工程(上)OpenGrok简介有些情况下,我们需要在GB级别甚至几十GB级别且随时不断更新的大型代码(比如Android源码)里面搜索阅读源码,那么我们自然而然会有以下一些基本的需求:能够快速搜索代码代码可存放于本地/服务器代码之间可以跳转跨平台易于维护…显然SourceInsight、ctags、grep等工具在这些场景下就不太适合,于是有了OpenGrok这类代码搜索引擎。先看看维…

  • Django(64)频率认证源码分析与自定义频率认证「建议收藏」

    Django(64)频率认证源码分析与自定义频率认证「建议收藏」前言有时候我们发送手机验证码,会发现1分钟只能发送1次,这是做了频率限制,限制的时间次数,都由开发者自己决定频率认证源码分析defcheck_throttles(self,request):

  • 欧式距离计算公式

    欧式距离计算公式欧式距离也称欧几里得距离,是最常见的距离度量,衡量的是多维空间中两个点之间的绝对距离。也可以理解为:m维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离下面是具体的计算公式…

  • CNN 卷积神经网络结构

    CNN 卷积神经网络结构CNNcnn每一层会输出多个featuremap,每个FeatureMap通过一种卷积滤波器提取输入的一种特征,每个featuremap由多个神经元组成,假如某个featuremap的shape是m*n,则该featuremap有m*n个神经元。对于卷积层会有kernel,记录上一层的featuremap与当前层的卷积核的权重,因此kernel的shape为(上一层feature

  • Vue Router 的params和query传参的使用和区别(详尽)「建议收藏」

    Vue Router 的params和query传参的使用和区别(详尽)「建议收藏」首先简单来说明一下$router和$route的区别//$router:是路由操作对象,只写对象//$route:路由信息对象,只读对象//操作路由跳转this.$router.push({name:’hello’,params:{name:’word’,age:’11’}})//读取…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号