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)


相关推荐

  • selenium自动化测试报告_selenium自动化测试断言

    selenium自动化测试报告_selenium自动化测试断言自动化测试——selenium(完结篇)文章目录自动化测试——selenium(完结篇)一、元素操作方法二、浏览器操作方法三、获取元素信息操作四、鼠标操作(需要实例化鼠标对象)4.1鼠标右键及双击4.2鼠标拖拽4.3鼠标悬停【重点】五、键盘操作(不需要实例化对象)☆六、元素等待七、下拉框(需要实例化下拉框)八、弹出框九、滚动条十、切换frame表单☆10.1连续切换frame十一、多窗口的切换☆十二、截图操作十三、验证码前言:看这篇帖子,最好要在知道定位八大元素的基础之上才能够比较熟练

  • Hibernate连接mycat实现多租户[通俗易懂]

    Hibernate连接mycat实现多租户[通俗易懂]Hibernate连接mycat实现多租户

  • kong 简明介绍「建议收藏」

    kong 简明介绍「建议收藏」kong1、WhatareServicesandRoutes?1.1AddaService1.2AddaRoutekong官方文档:https://docs.konghq.com/getting-started-guide/2.4.x/overview/1、WhatareServicesandRoutes?在KongGateway中,Service是表示外部上游API或微服务的实体——例如,数据转换微服务、计费API等。Service的主要属性是它的U

  • HTML 动画(一)[通俗易懂]

    HTML 动画(一)[通俗易懂]入场动画(一)图片从左至右逐渐消失实现逻辑:a:将遮罩分割为数个div,多个div通过图片定位拼接成一张图片;b:运用requestAnimationFrame+animation实现动画;c:遮罩层网格状逐步消失设置background-position:0;2.效果图:3.代码:<!DOCTYPEhtml><htmllang=”en”&…

  • 软件著作权的源代码_手机桌面整理软件

    软件著作权的源代码_手机桌面整理软件《(最新整理)软件著作权-源代码范本》由会员分享,可在线阅读,更多相关《(最新整理)软件著作权-源代码范本(127页珍藏版)》请在人人文库网上搜索。1、完整)软件著作权-源代码范本(完整)软件著作权-源代码范本编辑整理:尊敬的读者朋友们:这里是精品文档编辑中心,本文档内容是由我和我的同事精心编辑整理后发布的,发布之前我们对文中内容进行仔细校对,但是难免会有疏漏的地方,但是任然希望((完整)软件著作…

  • EasyBoot使用方法

    EasyBoot使用方法1修改背景图片直接替换掉EasyBoot\disk1\ezboot目录下面的BACK.BMP文件即可。但是限于DOS功能限制,只能使用640×480像素,256位色的BMP图片。2鼠标

发表回复

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

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