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

Linux系统编程收尾(35)

文章目录

  • 前言
  • 一、读写锁
  • 二、自旋锁
  • 总结


前言

  大家好,这是我们Linux系统编程的最后一节课了!
  大家请再撑住一会儿~


一、读写锁

  提到读写锁,我们就不得不提到 读者写者模型 ,跟 生产者消费者模型 不同的是,本模型的核心思想是 读者共享,写者互斥

  这就好比博客发布了,允许很多人同时读,但如果作者想要进行修改,那么其他人自然也就无法查看了,这就是一个很典型的 读者写者 问题

读者写者模型 也遵循 321 原则

3 种关系:

  读者 <-> 读者 无关系
  写者 <-> 写者 互斥
  读者 <-> 写者 互斥、同步

2 种角色:读者、写者

1 个交易场所:阻塞队列或其他缓冲区

为什么读者与读者间甚至不存在互斥关系?
因为读者读取数据时,并不会对数据做出修改,因此不需要维持互斥关系

  pthread库里面提供了有关读写锁的一些接口

#include <pthread.h>pthread_rwlock_t; // 读写锁类型// 初始化读写锁
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_tryrdlock(pthread_rwlock_t *__rwlock); // 非阻塞式// 写者,加锁
int pthread_rwlock_wrlock(pthread_rwlock_t *__rwlock); // 阻塞式 
int pthread_rwlock_trywrlock(pthread_rwlock_t *__rwlock); // 非阻塞式// 解锁(读者锁、写者锁都可以解)
int pthread_rwlock_unlock(pthread_rwlock_t *__rwlock); 

  注意: 读者和写者使用的加锁接口并不是同一个

关于 读者写者模型 的实现:

  • 读者读数据时,允许其他读者一起读取数据,但不允许写者修改数据
  • 写者写数据时,不允许读者进入
  • 读者读取完数据后,通知写者进行写入
  • 写者写完数据后,通知读者进行读取

  因为现实中,读者数量大多数情况下都是多于写者的,所以势必会存在很多很多读者不断读取,导致写者根本申请不到信号量,写者陷入 死锁 状态

  这是读者写者模型的特性,也是 读者优先 策略的体现,如果想要避免死锁,可以选择 写者优先 策略,优先让写者先写,读者先等一等

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <cstdlib>
#include <ctime>// 共享资源
int shared_data = 0;// 读写锁
pthread_rwlock_t rwlock;// 读者线程函数
void* Reader(void* arg)
{//sleep(1); //读者优先,一旦读者进入 && 读者很多,写者基本就很难进入了int number = *(int *)arg;while (true){pthread_rwlock_rdlock(&rwlock); // 读者加锁std::cout << "读者-" << number << " 正在读取数据, 数据是: " << shared_data << std::endl;sleep(1);                       // 模拟读取操作pthread_rwlock_unlock(&rwlock); // 解锁}delete (int*)arg;return NULL;
}// 写者线程函数
void* Writer(void* arg)
{int number = *(int *)arg;while (true){pthread_rwlock_wrlock(&rwlock); // 写者加锁shared_data = rand() % 100;     // 修改共享数据std::cout << "写者- " << number << " 正在写入. 新的数据是: " << shared_data << std::endl;sleep(2);                       // 模拟写入操作pthread_rwlock_unlock(&rwlock); // 解锁}delete (int*)arg;return NULL;
}int main()
{srand(time(nullptr) ^ getpid());pthread_rwlock_init(&rwlock, NULL); // 初始化读写锁// 可以更高读写数量配比,观察现象const int reader_num = 2;const int writer_num = 2;const int total = reader_num + writer_num;pthread_t threads[total]; // 假设读者和写者数量相等// 创建读者线程for (int i = 0; i < reader_num; ++i){int *id = new int(i);pthread_create(&threads[i], NULL, Reader, id);}// 创建写者线程for (int i = reader_num; i < total; ++i){int *id = new int(i - reader_num);pthread_create(&threads[i], NULL, Writer, id);}// 等待所有线程完成for (int i = 0; i < total; ++i){pthread_join(threads[i], NULL);}pthread_rwlock_destroy(&rwlock); // 销毁读写锁return 0;
}

  现在我将以 生活化 的例子来帮大家理解这段代码

  首先,先用图书馆来比喻什么是读者,什么是写者
在这里插入图片描述

共享数据与锁初始化

在这里插入图片描述

读者线程

在这里插入图片描述

写者线程

在这里插入图片描述

主线程

在这里插入图片描述

锁的三种状态

在这里插入图片描述

二、自旋锁

  接口大致浏览~

  自旋锁:申请锁失败时,线程不会被挂起,而且不断尝试申请锁

  自旋 本质上就是一个不断 轮询 的过程,即不断尝试申请锁,这种操作是十分消耗 CPU 时间的,因此推荐临界区中的操作时间较短时,使用 自旋锁 以提高效率;操作时间较长时,自旋锁 会严重占用 CPU 时间

  自旋锁 的优点:可以减少线程切换的消耗

#include <pthread.h>pthread_spinlock_t lock; // 自旋锁类型int pthread_spin_init(pthread_spinlock_t *lock, int pshared); // 初始化自旋锁int pthread_spin_destroy(pthread_spinlock_t *lock); // 销毁自旋锁// 自旋锁加锁
int pthread_spin_lock(pthread_spinlock_t *lock); // 失败就不断重试(阻塞式)
int pthread_spin_trylock(pthread_spinlock_t *lock); // 失败就继续向后运行(非阻塞式)// 自旋锁解锁
int pthread_spin_unlock(pthread_spinlock_t *lock);

  现在我们再次借用 DS 来用比喻助于你的理解

在这里插入图片描述

// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>int ticket = 1000;
pthread_spinlock_t lock;void* route(void* arg)
{char* id = (char*)arg;while (1){pthread_spin_lock(&lock);if (ticket > 0){usleep(1000);printf("%s sells ticket:%d\n", id, ticket);ticket--;pthread_spin_unlock(&lock);}else{pthread_spin_unlock(&lock);break;}}return nullptr;
}int main(void)
{pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);pthread_t t1, t2, t3, t4;pthread_create(&t1, NULL, route, (void *)"thread 1");pthread_create(&t2, NULL, route, (void *)"thread 2");pthread_create(&t3, NULL, route, (void *)"thread 3");pthread_create(&t4, NULL, route, (void *)"thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);pthread_spin_destroy(&lock);return 0;
}

在这里插入图片描述
  上面是DS大人给出的比喻,下面是其给出的对比表

在这里插入图片描述


总结

  结束喽,Linux系统编程环节,现在是即将进入Linux网络编程!!!

相关文章:

  • 零硬件成本玩转嵌入式通信!嵌入式仿真实验教学平台解锁STM8S串口黑科技
  • keepalived定制日志bug
  • 轻量级swiper插件推荐
  • 2025陕西省赛补题
  • [python] 最大公约数 和 最小公倍数
  • Dungeon Master(POJ-2251)
  • 现代密码学入门 | 现代密码学核心特点介绍
  • DeepSeek-R1 重磅升级,智能体验再进化!
  • antDesignVue中a-upload上传组件的使用
  • 算法打卡第11天
  • 小工具合集
  • 无人机视角海上漂浮物检测与人员救援检测数据集VOC+YOLO格式2903张6类别
  • 2024 CKA模拟系统制作 | Step-By-Step | 18、题目搭建-备份还原Etcd
  • sward V1.1.4版本发布,支持文档审批及文档导出
  • day40python打卡
  • Linux研学-入门命令
  • 经营分析会,财务该怎么做?
  • web自动化-Selenium、Playwright、Robot Framework等自动化框架使用场景优劣对比
  • 从零打造算法题刷题助手:Agent搭建保姆级攻略
  • 【位运算】常见位运算总结
  • 58企业网站如何做/北京seo多少钱
  • 网页 网站及与之相关的概念/如何推广品牌知名度
  • 做网站服务销售/app拉新项目
  • 外国网站怎么做/网站免费下载安装
  • 华春建设工程项目管理有限公司网站/自媒体平台收益排行榜
  • 开发公司经理述职报告/成都百度seo优化公司