Linux多线程实践(6) --Posix读写锁解决读者写者问题
Posix读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
读写锁与互斥量类似, 不过读写锁允许更高的并行性. 读写锁用于读称为共享锁, 读写锁用于写称为排它锁;
读写锁规则:
只要没有线程持有给定的读写锁用于写, 那么任意数目的线程可以持有读写锁用于读;
仅当没有线程持有某个给定的读写锁用于读或用于写时, 才能分配读写锁用于写;
Posix自旋锁
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);int pthread_spin_unlock(pthread_spinlock_t *lock);
自旋锁类似于互斥锁, 它的性能比互斥锁更高;
自旋锁与互斥锁很重要的一个区别在于: 线程在申请自旋锁的时候, 线程并不会挂起, 它总是处于忙等待的状态(一直在自旋, CPU处于空耗的状态);
自旋锁可用于以下情况:锁被持有的时间短, 而且线程并不希望在重新调度上花费太多的成本.
自旋锁通常作为底层原语用于实现其他类型的锁: 比如有些互斥锁的实现在试图获取互斥量的时候会自旋一小段时间, 只有在自旋计数到达某一阈值的时候才会休眠; 因此, 很多互斥量的实现非常搞笑, 以至于应用程序采用互斥锁的性能与曾经采用过自旋锁的性能基本上是相同的.
因此, 自旋锁只在某些特定的情况下有用, 比如在用户层, 自旋锁并不是非常有用, 除非运行在不允许抢占的实时调度类中.
读者写者问题
问题描述
一个数据对象可以为多个并发进程所共享。其中有的进程可能只需要读共享对象的内容,而其他进程可能要更新共享对象的内容。
读者:只对读感兴趣的进程;
写者:其他进程(只写,或既读又写);
规则
允许多个读者同时读取数据;
只有一个写者可以写数据;
写者在写时读者不能读,反之亦然。
/** 实现1: 运用读写锁解决”读者写者问题”
解题思路: 将需要读写的文件实现为一个字符串;
读者进程: 一次可以将该字符串全部读出, 然后打印读取信息
写者进程: 一次只能修改一个字符(该字符从A~Z循环写入), 修改之后打印写入信息
**/
//读写锁
pthread_rwlock_t rwlock;
const unsigned READERCOUNT = 2; //读者数
const unsigned WRITERCONUT = 5; //写者数const int PAPERSIZE = 32; //文件长度
char paper[PAPERSIZE+1]; //文件unsigned short int write_index = 0; //写者需要写入的位置
char ch = 'A'; //写者需要写入的字母pthread_t thread[READERCOUNT+WRITERCONUT]; //读者+写者线程//读者线程
void *reader(void *args)
{int number = *(int *)args;delete (int *)args;while (true){//获取共享锁pthread_rwlock_rdlock(&rwlock);//开始读printf("## reader %d was reading...\n", number);printf("text: %s\n", paper);printf(" reader %d end reading...\n", number);//解锁共享锁pthread_rwlock_unlock(&rwlock);sleep(1);}pthread_exit(NULL);
}
//写者线程
void *writer(void *args)
{int number = *(int *)args;delete (int *)args;while (true){//获取写锁pthread_rwlock_wrlock(&rwlock);//开始写printf("++ writer %d was writing...\n", number);paper[write_index] = ch;write_index = (write_index+1)%PAPERSIZE;ch = ch+1;if (ch > 'Z')ch = 'A';printf(" writer %d end writing...\n", number);//释放写锁pthread_rwlock_unlock(&rwlock);sleep(1);}pthread_exit(NULL);
}int main()
{memset(paper, 0, sizeof(paper));pthread_rwlock_init(&rwlock, NULL);for (unsigned int i = 0; i < READERCOUNT; ++i)pthread_create(&thread[i], NULL, reader, new int(i));for (unsigned int i = 0; i < WRITERCONUT; ++i)pthread_create(&thread[READERCOUNT+i], NULL, writer, new int(i));for (unsigned int i = 0; i < READERCOUNT+WRITERCONUT; ++i)pthread_join(thread[i], NULL);pthread_rwlock_destroy(&rwlock);
}
/** 实现2: 运用Posix信号量使用”读者优先”策略解决”读者写者问题”
解题思路:
如果新读者到:①无读者、写者,新读者可以读;②有写者等待,但有其它读者正在读,则新读者也可以读;③有写者写,新读者等待。
如果新写者到:①无读者,新写者可以写;②有读者,新写者等待;③有其它写者,新写者等待。
**/
// 需要用两个互斥量实现
pthread_mutex_t rmutex;
pthread_mutex_t wmutex;const unsigned READERCOUNT = 5; //读者数
const unsigned WRITERCONUT = 5; //写者数const int PAPERSIZE = 32; //文件长度
char paper[PAPERSIZE+1]; //文件unsigned short int write_index = 0; //写者需要写入的位置
char ch = 'A'; //写者需要写入的字母pthread_t thread[READERCOUNT+WRITERCONUT]; //读者+写者线程int nReader = 0;
//读者线程
void *reader(void *args)
{int number = *(int *)args;delete (int *)args;while (true){pthread_mutex_lock(&rmutex);//如果是第一个读者, 则锁定wmutexif (nReader == 0)pthread_mutex_lock(&wmutex);++ nReader;pthread_mutex_unlock(&rmutex);//开始读printf("## reader %d was reading...\n", number);printf("text: %s\n", paper);printf(" reader %d end reading...\n\n", number);pthread_mutex_lock(&rmutex);-- nReader;//如果是最后一个读者, 则解锁wmutexif (nReader == 0)pthread_mutex_unlock(&wmutex);pthread_mutex_unlock(&rmutex);sleep(1);}pthread_exit(NULL);
}//写者线程
void *writer(void *args)
{int number = *(int *)args;delete (int *)args;while (true){//获取写锁pthread_mutex_lock(&wmutex);//开始写printf("++ writer %d was writing...\n", number);paper[write_index] = ch;write_index = (write_index+1)%PAPERSIZE;ch = ch+1;if (ch > 'Z')ch = 'A';printf(" writer %d end writing...\n\n", number);//释放写锁pthread_mutex_unlock(&wmutex);sleep(1);}pthread_exit(NULL);
}int main()
{memset(paper, 0, sizeof(paper));pthread_mutex_init(&rmutex, NULL);pthread_mutex_init(&wmutex, NULL);for (unsigned int i = 0; i < READERCOUNT; ++i)pthread_create(&thread[i], NULL, reader, new int(i));for (unsigned int i = 0; i < WRITERCONUT; ++i)pthread_create(&thread[READERCOUNT+i], NULL, writer, new int(i));for (unsigned int i = 0; i < READERCOUNT+WRITERCONUT; ++i)pthread_join(thread[i], NULL);pthread_mutex_destroy(&rmutex);pthread_mutex_destroy(&wmutex);
}
“读者优先”思想小结: 读者优先的设计思想是读进程只要看到有其它读进程正在读,就可以继续进行读;写进程必须等待所有读进程都不读时才能写,即使写进程可能比一些读进程更早提出申请。该算法只要还有一个读者在活动,就允许后续的读者进来,该策略的结果是,如果有一个稳定的读者流存在,那么这些读者将在到达后被允许进入。而写者就始终被挂起,直到没有读者为止.
相关文章:

iOS-直播开发(开发从底层做起)
代码链接: Github: https://github.com/jessonliu/JFLivePlaye 技术部分------ ⬇️ 脑涂: ![ 直播思维导图.png ] 视频直播的大概流程就上脑涂上所画的, 还有一些没列出来, 比如, 聊天, 送礼, 踢出, 禁言, 等等一系列功能, 但本文只是针对视频直播的简单实现! 下边来说一下以…

汇编程序设计与计算机体系结构软件工程师教程笔记:函数、字符串、浮点运算
《汇编程序设计与计算机体系结构: 软件工程师教程》这本书是由Brain R.Hall和Kevin J.Slonka著,由爱飞翔译。中文版是2019年出版的。个人感觉这本书真不错,书中介绍了三种汇编器GAS、NASM、MASM异同,全部示例代码都放在了GitHub上,…
《庆余年》值得一看吗?Python告诉你谁在关注 | CSDN原力计划
扫码参与CSDN“原力计划”作者 | A字头来源 | 数据札记倌庆余年电视剧终于在前两天上了,这两天赶紧爬取数据看一下它的表现。庆余年《庆余年》是作家猫腻的小说。这部从2007年就开更的作品拥有固定的书迷群体,也在文学IP价值榜上有名。期待已久的影视版的…

《C语言及程序设计》实践项目——画分支结构流程图
返回:贺老师课程教学链接 【单分支结构流程图-大值】问题:画流程图,输入两个整数a和b,输出其中的大值。提示:当a<b时,交换a和b,最后输出的a一定是其中的大值。流程图中可以直接给出交换a和b…

汇编程序设计与计算机体系结构软件工程师教程笔记:内联汇编与宏
《汇编程序设计与计算机体系结构: 软件工程师教程》这本书是由Brain R.Hall和Kevin J.Slonka著,由爱飞翔译。中文版是2019年出版的。个人感觉这本书真不错,书中介绍了三种汇编器GAS、NASM、MASM异同,全部示例代码都放在了GitHub上,…
无需标注数据,利用辅助性旋转损失的自监督GANs,效果堪比现有最好方法
作者 | Ting Chen译者 | 王红成出品 | AI科技大本营(ID:rgznai100)本文作者提出了一种自检督方式的生成对抗网络,通过辅助性的旋转损失来达到目的。因为通常主流方法来生成自然图像都是通过条件GAN来完成,但是这就需要很多的标签数…

iOS环信聊天界面中点击头像和消息的几种状态
/*环信自带头像点击事件*/ - (void)messageViewController:(EaseMessageViewController *)viewControllerdidSelectAvatarMessageModel:(id<IMessageModel>)messageModel {内容可以根据需要自己添加 }/*!methodbrief 点击了简历消息 (lyq添加)discussion 点击了简历消息,…

ASP.NET将Session保存到数据库中
因为ASP.NET中Session的存取机制与ASP相同,都是保存在进行中, 一旦进程崩溃,所有Session信息将会丢失,所以我采取了将Session信息保存到SQL Server中,尽管还有其它的几个方式(本文不作介绍)&…

iOS App上架流程
一、前言:作为一名iOSer,把开发出来的App上传到App Store是必要的。下面就来详细讲解一下具体流程步骤。 二、准备: 一个已付费的开发者账号(账号类型分为个人(Individual)、公司(Company&#…
不止Markov决策过程,全景式分析强化学习研究内容
作者 | 肖智清编辑 | 刘静来源 | CSDN(ID:CSDNnews)强化学习作为通用人工智能的希望,吸引了很多人工智能爱好者学习和研究。Markov决策过程是最知名的强化学习模型,强化学习教程也常以Markov决策过程作为起点。但是&am…

Windows下创建进程简介
正在执行的应用程序称为进程,进程不仅仅是指令和数据,它还有状态。状态是保存在处理器寄存器中的一组值,如当前执行指令的地址、保存在内存中的值,以及唯一定义进程在任一时刻任务的所有其他值。进程与应用程序的一个重要的区别在…

jQuery中鲜为人知的的几个方法
转来学习一下 jQuery中鲜为人知的的几个方法 jQuery近些年来仍旧是web开发中最受欢迎的类库,虽然大家褒贬不一,但是仍旧不失为一款最流行的Javascript,在今天这篇文章中,我们将介绍几个jQuery的相关方法,无论你是入门级…

Linux下创建进程简介
在博文https://blog.csdn.net/fengbingchun/article/details/108940548中简单介绍了Windows下通过函数CreateProcess创建进程的过程,这里简单介绍下Linux下通过fork函数创建进程的过程。很早之前在https://blog.csdn.net/fengbingchun/article/details/45690745中也…

热更新 FrameWork
工作中遇到想要绕过AppStore直接更新App的要求!这里友情提示下,看了很多资料只是是实现了功能,但在项目中并没有真正用到!资料大多都显示会被拒,这个说的是个人级的,好像企业级的不会这样,仅仅是项目需要做…
陆首群:评人工智能如何走向新阶段?
作者 | 陆首群,中国开源软件推进联盟名誉主席 出品 | AI科技大本营(ID:rgznai100) 编者按:近来,业内关于深度学习算法的潜力是否已达天花板的争论陆续发出。有人认为,基于深度学习算法的应用还有深度开拓空…

Mysql INSERT、REPLACE、UPDATE的区别
用于操作数据库的SQL一般分为两种,一种是查询语句,也就是我们所说的SELECT语句,另外一种就是更新语句,也叫做数据操作语句。言外之意,就是对数据进行修改。在标准的SQL中有3个语句,它们是INSERT、UPDATE以及…

软件调试的艺术笔记:GDB
很久之前,在https://blog.csdn.net/fengbingchun/article/details/41413381中简单整理过gdb中常用的一些命令,不齐全,这里按照《软件调试的艺术》一书中关于gdb的介绍再做次整理。《软件调试的艺术》于2009年由人民邮电出版社出版。 1. 预备…

App Store 审核被拒整理
整理以前和现在遇到的审核被拒第一:2.2 DetailsWe discovered one or more bugs in your app when reviewed on iPhone running iOS 8.1.3 on both Wi-Fi and cellular networks. Specifically, we were not able to complete the In App Purchase. When tapped on …
只服这篇“神文”:基于老子哲学、相对论的超级人工智能模型
作者 | Anonymous authors译者 | TroyChang出品 | AI科技大本营(ID:rgznai100)在此前我们为大家介绍 ICLR 2020 论文投稿情况时,提到了一篇“神作”在论文中作者们提出一个 ASI 概念(Artificial Super Intelligence&am…

Navicat Premium使用教程【比较详细】
Navicat Premium使用教程简介:Navicat Premium是众所周知的数据库操作软件。本文比较详细。1、打开Navicat Premium,点击连接,选择MySQL,创建新连接。输入安装MySQL是的用户名和密码。点击确定。2、admin数据连接已经创建成功。下…
如何用Neo4j和Scikit-Learn做机器学习任务?| 附超详细分步教程
作者 | Mark Needham译者 | Tianyu、Shawnice编辑 | Jane出品 | AI科技大本营(ID:rgznai100)图算法不是一个新兴技术领域,在开源库中已经有很多功能强大的算法实现。近两年,业内的学者与科学家都在积极探索可以弥补深度…

Docker在Ubuntu16.04和Windows10家庭版上安装操作步骤
之前在 https://blog.csdn.net/fengbingchun/article/details/109559500 中对Docker作了简单的介绍,这里介绍下Docker在Ubuntu16.04 x86_64 64位上和Windows10 x86_64 64位家庭版上的安装过程。 在Ubuntu上安装Docker(或Docker引擎),Ubuntu必须是64位的…

iOS 不同机型屏幕适配
// .pch 文件中写 // 判断是iPhone机型 /** 4s 960 * 640* 5/5s 1136 x 640* 6/6s/7/8 4.7英寸 1334 x 750* 6p/6sp/7p/8p 5.5英寸 1920 x 1080* X 5.8英寸 2436 x 1125}*/ #define IS_IPHONE_4s [UIScreen instancesRespondToSelector:selector(currentMode)] ? \ CGSizeEqua…

北京中天荣泰视觉检测 仿真
www.romtek.cnhttp://jobs.zhaopin.com/191485013250433.htm?ssidkeyy&ff01&ss101转载于:https://www.cnblogs.com/pengkunfan/p/4316018.html

Docker客户端常用命令整理
之前在 https://blog.csdn.net/fengbingchun/article/details/109584460 中介绍过在Windows10家庭版和Ubuntu16.04上安装Docker的操作步骤,这里整理下Docker客户端常用命令。 在Windows10家庭版上运行Docker后,通过VMware就不能打开Ubuntu16.04虚拟机了…
深度学习入门笔记,三流程序员如何凭借实力逆袭高薪?你不服不行!
最近经常有朋友提及,想要入门深度学习,该如何学习?关于深度学习,网上的资料很多,不过貌似大部分都不太适合初学者。 我曾经是一名三流程序员,每天的工作内容就是在前人留下的 bug 上写新的bug,我…

Swift编程语言
The Swift Programming Language中文手册1.【精校版】The Swift Programming Language--欢迎使用Swift--关于Swift2.【精校版】The Swift Programming Language-欢迎使用Swift-Swift 初见3.The Swift Programming Language--语言指南--基础部分4.The Swift Programming Languag…

ondblog 修改informix日志模式
-N No Logging 没有日志-U Unbuffered Logging 非缓冲日志-B Buffered Logging 缓冲日志-A Unbuffered Logging, Mode ANSI ANSI模式No Logging 没有日志----“没有日志”模式只向逻辑日志写很少的信息,它只记录执行的DDL语句,这些语句影响到的行并…

iOS RunLoop详解
一、简介 CFRunLoopRef源码RunLoop是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸事件、UI刷新事件、定时器事件、Selector事件),从而保持程序的持续运行;而且在没有事件处理的时候&…

开源库jemalloc简介
jemalloc是通用的malloc(3)实现,它强调避免碎片和可扩展的并发支持。它的源码位于https://github.com/jemalloc/jemalloc,最新稳定版本为5.2.1。 glibc的内存分配算法是基于dlmalloc实现的ptmalloc;tcmalloc是Google开发的内存分配器&#x…