当前位置: 首页 > 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;
}

http://www.lryc.cn/news/471420.html

相关文章:

  • JavaScript 的 axios 实现文件下载功能
  • 合合信息亮相2024中国模式识别与计算机视觉大会,用AI构建图像内容安全防线
  • 深度学习:匿名函数lambda函数的使用与numerical_gradient函数
  • PHP数据类型
  • 2FA-双因素认证
  • 解决 Python 中的 TypeError 错误
  • 快速学会C 语言基本概念和语法结构
  • Python酷库之旅-第三方库Pandas(172)
  • Linux系统下minio设置SSL证书进行HTTPS远程连接访问
  • npm 包的命名空间介绍,以及@typescript-eslint/typescript-eslint
  • ecovadis评估是什么,有什么提成自己评分等级
  • Vue3中ref、toRef和toRefs之间有什么区别?
  • react开发技巧
  • HarmonyOS第一课——HarmonyOS介绍
  • XCode16中c++头文件找不到解决办法
  • CSS - 保姆级面试基础扫盲版本一
  • 51c自动驾驶~合集2
  • Redis后台任务有哪些
  • TPair<TKey, TValue> 键值对
  • 【杂谈】城市规划教育的危与机
  • 金融工程--pine-script 入门
  • Vue3 跨标签页或跨窗口通信
  • Ollama: 使用Langchain的OllamaFunctions
  • java质数的判断 C语言指针变量的使用
  • TensorFlow面试整理-TensorFlow 数据处理
  • vue路由的基本使用
  • 数据结构分类
  • 【STM32】 TCP/IP通信协议--LwIP介绍
  • 一些面试题整理
  • 端口号和ip地址一样吗?区别是什么