当前位置: 首页 > news >正文

Linux 读者写者模型

1.背景概念

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。这样就衍生出了读者写者模型,在这个模型中,有两类线程:读者和写者。读者线程读取共享资源,而写者线程则修改共享资源,读者和读者之间为共享关系,写者和写者为同步互斥关系,读者和写者之间为互斥关系。
该模型有两种策略:

读者优先策略

在读者优先策略中,允许多个读者同时访问共享资源,有读者在读时,写者必须等待,直到所有当前的读者和写者都完成访问。这种策略可以提高并发度,但可能导致写者饥饿。

写者优先策略

在写者优先策略中,一旦有写者等待,新的读者必须等待,直到所有当前的读者和写者都完成访问。这种策略可以减少写者的等待时间,但可能导致读者饥饿。

通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 在Linux系统中,可以使用信号量(Semaphore)或读写锁(Read-Write Lock)来解决读者写者问题。信号量可以用来实现互斥访问,而读写锁则可以允许多个读者同时访问,但在有写者等待时,新的读者必须等待,接下来我们介绍读写锁。

2.读写锁

  • 读模式(共享模式)
    • 多个线程可以同时以读模式持有读写锁。当一个线程以读模式获取到读写锁后,其他线程如果也想要以读模式获取该锁,是可以成功获取的。这是因为多个读操作通常不会相互干扰,不会破坏数据的一致性。例如,多个线程同时读取一个共享文件的内容,它们可以同时进行,不会有冲突。
  • 写模式(独占模式)
    • 只有一个线程可以以写模式持有读写锁。当一个线程以写模式获取到读写锁时,其他任何线程(无论是想要以读模式还是写模式获取该锁)都必须等待。这是因为写操作会修改共享资源,如果有其他线程同时进行读或写操作,可能会导致数据不一致。例如,当一个线程正在修改一个共享文件时,不允许其他线程同时读取或修改该文件。

在Linux中,读写锁的类型为 pthread_rwlock_t,其使用方法与互斥锁基本一致。

读写锁的创建

1. 可以使用PTHREAD_RWLOCK_INITIALIZER初始化创建全局的读写锁,并且无需销毁。

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
2. 使用 pthread_rwlock_init 函数初始化读写锁。
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);

参数:

  • rwlock:指向要初始化的读写锁对象的指针。
  • attr:指向读写锁属性对象的指针,如果不需要特殊属性,可以设置为 NULL,使用默认属性。

函数成功时返回 0;失败时返回错误码。

加锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

pthread_rwlock_rdlock 用于给读写锁申请一个读锁。如果读写锁当前没有被写锁占用,那么调用线程可以立即获得读锁。如果读写锁当前被写锁占用,那么调用线程将被阻塞,直到写锁被释放。

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

pthread_rwlock_wrlock 用于给读写锁申请一个写锁。如果读写锁当前没有被任何锁占用,那么调用线程可以立即获得写锁。如果读写锁当前被读锁或写锁占用,那么调用线程将被阻塞,直到所有的读锁和写锁都被释放。

rwlock:指向要申请读锁的读写锁对象的指针。

解锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

pthread_rwlock_unlock 函数用于释放之前通过 pthread_rwlock_rdlock(读锁)或者 pthread_rwlock_wrlock(写锁)获取的读写锁。

读写锁的销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

3.基于读写锁实现的读者写者模型

基于读写锁,我们来实现一个测试代码看看效果

#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
using namespace std;
int g_tickets = 100;
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 读写锁
struct ThreadData
{pthread_t tid;string name;
};
void *ReaderRoutinue(void *arg)
{ThreadData *reader = static_cast<ThreadData *>(arg);while (true){usleep(1000);pthread_rwlock_rdlock(&rwlock); // 读锁if (g_tickets > 0)cout << reader->name << " check a ticket-" << g_tickets << endl;else{pthread_rwlock_unlock(&rwlock);break;}pthread_rwlock_unlock(&rwlock);}return nullptr;
}
void *WriterRoutinue(void *arg)
{ThreadData *writer = static_cast<ThreadData *>(arg);while (true){usleep(2000);pthread_rwlock_wrlock(&rwlock); // 写锁if (g_tickets > 0)cout << writer->name << " get a ticket-" << g_tickets-- << endl;else{pthread_rwlock_unlock(&rwlock);break;}pthread_rwlock_unlock(&rwlock);}return nullptr;
}
int main()
{ThreadData readers[5];ThreadData writers[2];for (int i = 0; i < 5; i++){readers[i].name = "Reader-" + to_string(i + 1);pthread_create(&readers[i].tid, nullptr, ReaderRoutinue, &readers[i]);}for (int i = 0; i < 2; i++){writers[i].name = "Writer-" + to_string(i + 1);pthread_create(&writers[i].tid, nullptr, WriterRoutinue, &writers[i]);}for (int i = 0; i < 5; i++)pthread_join(readers[i].tid, nullptr);for (int i = 0; i < 2; i++)pthread_join(writers[i].tid, nullptr);return 0;
}

相关文章:

Linux 读者写者模型

1.背景概念 在编写多线程的时候&#xff0c;有一种情况是十分常见的。那就是&#xff0c;有些公共数据修改的机会比较少。相比较改写&#xff0c;它们读的机会反而高的多。这样就衍生出了读者写者模型&#xff0c;在这个模型中&#xff0c;有两类线程&#xff1a;读者和写者。读…...

JavaScript 的 axios 实现文件下载功能

用 JavaScript 的 axios 实现文件下载功能&#xff0c;咱们要分几个步骤来搞定它&#xff01;最主要的部分是处理 二进制数据&#xff0c;可以生成一个进度检测&#xff0c;然后把它保存为文件。 文件名的获取二进制数据获取创建下载链接 const axios require(axios);const g…...

合合信息亮相2024中国模式识别与计算机视觉大会,用AI构建图像内容安全防线

近日&#xff0c;第七届中国模式识别与计算机视觉大会&#xff08;简称“PRCV 2024”&#xff09;在乌鲁木齐举办。大会由中国自动化学会&#xff08;CAA&#xff09;、中国图象图形学学会&#xff08;CSIG&#xff09;、中国人工智能学会&#xff08;CAAI&#xff09;和中国计…...

深度学习:匿名函数lambda函数的使用与numerical_gradient函数

背景&#xff1a; 假设我们有一个简单的线性回归模型&#xff0c;其损失函数是均方误差&#xff08;MSE&#xff09;&#xff1a; class LinearModel:def __init__(self):self.W np.random.randn(1, 1) # 初始化权重def predict(self, x):return np.dot(x, self.W) # 线性预…...

PHP数据类型

几种常用的数据类型&#xff1a; String&#xff08;字符串&#xff09; Integer&#xff08;整型&#xff09; Float&#xff08;浮点型&#xff09; Boolean&#xff08;布尔型&#xff09; NULL&#xff08;空值&#xff09; Array&#xff08;数组&#xff09; Obje…...

2FA-双因素认证

双因素认证&#xff08;2FA&#xff0c;Two-Factor Authentication&#xff09;是一种提高安全性的方法&#xff0c;要求用户在登录或进行某些敏感操作时提供两种不同类型的身份验证信息。这种方法通过引入第二层验证&#xff0c;增加了账户被未经授权访问的难度。 项目结构 …...

解决 Python 中的 TypeError 错误

解决 Python 中的 TypeError 错误 在 Python 编程中&#xff0c;TypeError 是一种常见的错误&#xff0c;通常发生在尝试对不兼容的类型进行操作时。了解这个错误的原因以及如何有效解决它&#xff0c;对于提高代码的可靠性和可读性至关重要。本文将详细讨论 TypeError 的成因…...

快速学会C 语言基本概念和语法结构

&#x1f600;前言 本篇博文是关于C 语言的基本概念和语法结构&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&a…...

Python酷库之旅-第三方库Pandas(172)

目录 一、用法精讲 791、pandas.UInt8Dtype类 791-1、语法 791-2、参数 791-3、功能 791-4、返回值 791-5、说明 791-6、用法 791-6-1、数据准备 791-6-2、代码示例 791-6-3、结果输出 792、pandas.UInt16Dtype类 792-1、语法 792-2、参数 792-3、功能 792-4、…...

Linux系统下minio设置SSL证书进行HTTPS远程连接访问

文章目录 1.配置SSL证书使用HTTPS访问2.MINIO SDK 忽略证书验证3.使用受信任的证书 1.配置SSL证书使用HTTPS访问 生成域名对应的SSL证书&#xff0c;下载Apache版本&#xff0c;我目前只发现Apache这个里面有对应的私钥和证书 私钥重命名为private.key证书重命名为public.crt&…...

npm 包的命名空间介绍,以及@typescript-eslint/typescript-eslint

npm 包的命名空间是一个重要的概念&#xff0c;用于组织和管理相关的包。通过命名空间&#xff0c;开发者可以避免命名冲突、增强包的可读性和可维护性。以下是关于 npm 命名空间的详细介绍&#xff0c;并以 typescript-eslint 作为示例。 1. 命名空间的结构 命名空间的格式为…...

ecovadis评估是什么,有什么提成自己评分等级

EcoVadis评估是一个企业社会责任&#xff08;CSR&#xff09;评级平台&#xff0c;旨在评估全球供应链的可持续性和道德情况。以下是对EcoVadis评估的详细介绍以及提升其评分等级的方法&#xff1a; 一、EcoVadis评估概述 定义&#xff1a;EcoVadis评估通过一系列框架评估公司…...

Vue3中ref、toRef和toRefs之间有什么区别?

前言 Vue 3 引入了组合式 API&#xff0c;其中 ref、toRef 和 toRefs 是处理响应式数据的核心工具。作为高级计算机工程师&#xff0c;我们有必要深入理解这些工具的细微差别&#xff0c;以便在实际项目中更加高效地管理状态。本文将详细解析 ref、toRef 和 toRefs 的区别&…...

react开发技巧

/* eslint-disable no-useless-escape */ const Validator { isEmail: /^([a-zA-Z0-9_\.\-])\(([a-zA-Z0-9\-])\.)([a-zA-Z0-9]{2,4})$/, // 校验邮箱 isPhoneNumber: /^1[3456789]\d{9}$/, // 手机号码验证 isMobileNumber: /^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/, //…...

HarmonyOS第一课——HarmonyOS介绍

HarmonyOS第一课 HarmonyOS介绍 HarmonyOS是新一代的智能终端操作系统&#xff08;泛终端服务的载体&#xff09;&#xff1b; 智慧互联协同&#xff0c;全场景交互体验&#xff1b; 核心技术理念&#xff1a; 一次开发 多次部署&#xff1a; 预览 可视化开发UI适配 事件交…...

XCode16中c++头文件找不到解决办法

XCode16中新建Framework&#xff0c;写完自己的c代码后&#xff0c;提示“<string> file not found”等诸如此类找不到c头文件的错误。 工程结构如下&#xff1a; App是测试应用&#xff0c;BoostMath是Framework。基本结构可以参考官方demo&#xff1a;Mix Swift and …...

CSS - 保姆级面试基础扫盲版本一

盒子模型 盒子模型定义&#xff1a; 当对一个盒子模型进行文档布局的时候&#xff0c;浏览器的渲染引擎会根据标准之一的CSS盒子模型&#xff08;CSS basic box model&#xff09;&#xff0c;将所有元素表示成一个个矩阵盒子。 一个盒子通常由四部分组成&#xff1a;border p…...

51c自动驾驶~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/11491137 #BEVWorld BEV潜在空间构建多模态世界模型&#xff0c;全面理解自动驾驶~一、引言 世界模型建模了有关环境的知识&#xff0c;其可以通过给定的条件对未来进行合理的想象。未来想象要求世界模型具有物理规律的理解…...

Redis后台任务有哪些

Redis后台任务 为了有更好的性能表现&#xff0c;redis对于一些比较耗时的操作会异步执行&#xff0c;不阻塞线上请求。文章从源码(redis7.0)来看&#xff0c;aof、rdb文件的关闭&#xff0c;aof文件的刷盘以及部分内存释放会采用异步方式&#xff0c;在后台线程中执行。接下来…...

TPair<TKey, TValue> 键值对

在 Delphi&#xff08;或更准确地说是 Object Pascal&#xff0c;Delphi 的编程语言&#xff09;中&#xff0c;TList<T> 是泛型列表的一个实现&#xff0c;其中 T 是列表中元素的类型。TPair<TKey, TValue> 是一个包含两个元素的记录&#xff08;record&#xff0…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

【UE5 C++】通过文件对话框获取选择文件的路径

目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 &#xff0c;这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器&#xff0c;右键点击 .uproject 文件&#xff0c;选择 "Generate Visual Studio project files"&#xff0c;重…...

Qt学习及使用_第1部分_认识Qt---Qt开发基本流程

前言 学以致用,通过QT框架的学习,一边实践,一边探索编程的方方面面. 参考书:<Qt 6 C开发指南>(以下称"本书") 标识说明:概念用粗体倾斜.重点内容用(加粗黑体)---重点内容(红字)---重点内容(加粗红字), 本书原话内容用深蓝色标识,比较重要的内容用加粗倾…...

软件工程教学评价

王海林老师您好。 您的《软件工程》课程成功地将宏观的理论与具体的实践相结合。上半学期的理论教学中&#xff0c;您通过丰富的实例&#xff0c;将“高内聚低耦合”、SOLID原则等抽象概念解释得十分透彻&#xff0c;让这些理论不再是停留在纸面的名词&#xff0c;而是可以指导…...

【JavaEE】万字详解HTTP协议

HTTP是什么&#xff1f;-----互联网的“快递小哥” 想象我们正在网上购物&#xff1a;打开淘宝APP&#xff0c;搜索“蓝牙耳机”&#xff0c;点击商品图片&#xff0c;然后下单付款。这一系列操作背后&#xff0c;其实有一个看不见的“快递小哥”在帮我们传递信息&#xff0c;…...