Linux --读者写者问题、读写锁与自旋锁
为什么需要读者写者问题在多线程编程中同步是一个永恒的话题。我们之前接触过生产者-消费者问题它描述的是生产者往缓冲区放数据消费者从缓冲区取数据两者需要互斥地访问缓冲区同时还要在缓冲区空/满时进行等待和唤醒。而读者写者问题是另一类经典的同步问题它的场景是有一块共享数据比如一个文件、一个数据库、一块内存。多个读者可以同时读取这块数据因为读操作不会改变数据所以读者之间不需要互斥。写者在写入数据时必须独占访问因为写操作会改变数据如果此时有其他读者或写者在访问就会导致数据不一致。写者与读者之间、写者与写者之间必须互斥。简单来说读共享写独占。读者写者 vs 生产者消费者对比项生产者-消费者读者-写者核心矛盾缓冲区空/满时的等待与唤醒读与写、写与写之间的互斥互斥关系生产者与消费者互斥访问缓冲区写者与所有人互斥读者之间不互斥同步关系生产者等待缓冲区有空位消费者等待缓冲区有数据读者等待写者离开写者等待所有读者离开典型场景消息队列、任务队列文件读写、数据库读写、配置中心重点理解读者写者问题的核心在于如何协调读者和写者之间的同步使得读操作可以并发写操作必须独占同时还要避免某一方“饿死”。读者写者问题的伪代码公共部分uint32_t reader_count 0; // 当前正在读取的读者数量 lock_t count_lock; // 保护 reader_count 的锁 lock_t writer_lock; // 写者锁读者和写者共享reader_count记录当前有多少个读者正在读。count_lock因为多个读者可能同时修改reader_count所以需要一把锁来保护它。writer_lock写者需要持有的锁同时第一个读者进入时也要持有它最后一个读者离开时释放它。Reader读者// 加锁 lock(count_lock); if (reader_count 0) lock(writer_lock); // 第一个读者锁住写者锁 reader_count; unlock(count_lock); // read; // 解锁 lock(count_lock); --reader_count; if (reader_count 0) unlock(writer_lock); // 最后一个读者释放写者锁 unlock(count_lock);逻辑解读读者先锁住count_lock然后检查自己是不是第一个读者。如果是第一个读者reader_count 0说明当前没有读者在读但可能有写者在写所以需要获取writer_lock。如果写者正在写读者会在这里阻塞直到写者释放writer_lock。然后reader_count表示自己开始读了释放count_lock。读取数据。读完后再次锁住count_lockreader_count--。如果自己是最后一个读者reader_count 0说明所有读者都离开了此时需要释放writer_lock让等待的写者可以进入。释放count_lock。关键点第一个读者负责“锁住”写者最后一个读者负责“释放”写者。中间的读者只是简单地增加/减少计数不会触碰writer_lock。Writer写者lock(writer_lock); // write unlock(writer_lock);逻辑解读写者直接尝试获取writer_lock。如果此时有读者正在读writer_lock被第一个读者持有写者会阻塞。如果此时有其他写者在写写者也会阻塞。获取到锁后独占写入。写完释放锁。注意这个伪代码实现的是读者优先策略。因为只要有一个读者持有writer_lock后续的读者都可以直接进入它们只需要count_lock而写者必须等待所有读者离开。如果读者源源不断写者可能永远等待即写者饥饿。读写锁pthread_rwlock在实际编程中我们不需要自己手动实现上述逻辑POSIX 提供了读写锁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);attr通常传NULL表示使用默认属性。使用前必须初始化使用后必须销毁。加锁与解锁int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 读锁 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 写锁 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 解锁读锁多个线程可以同时持有读锁。写锁同一时刻只能有一个线程持有写锁且此时不能有读锁。解锁无论是读锁还是写锁都用同一个unlock。设置读写优先级int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);pref有三种选择选项含义PTHREAD_RWLOCK_PREFER_READER_NP读者优先默认可能导致写者饥饿PTHREAD_RWLOCK_PREFER_WRITER_NP写者优先但目前有 BUG表现和读者优先一致PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP写者优先但写者不能递归加锁注意写者优先可以缓解写者饥饿但可能导致读者饥饿。实际使用时需要根据场景权衡。读者优先 vs 写者优先读者优先只要有读者在读后续读者可以直接进入写者必须等待所有读者离开。可能导致写者饥饿。写者优先一旦有写者到达后续读者会被阻塞直到写者完成。可能导致读者饥饿。选择建议如果读操作非常频繁写操作很少且写者饥饿不是问题可以用读者优先。如果写操作也很重要不能长时间等待可以用写者优先注意PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP。自旋锁Spinlock什么是自旋锁自旋锁是一种忙等待的锁。当一个线程尝试获取自旋锁但锁已被占用时它不会进入休眠而是在一个循环中不断检查锁是否可用。一旦锁被释放线程立即获取。类比你去上厕所发现门锁着你不会回去睡觉而是站在门口一直问“好了没好了没”直到里面的人出来。自旋锁的原理自旋锁通常使用一个原子标志位来表示锁的状态false锁可用。true锁已被占用。获取锁时使用CASCompare-And-Swap原子操作while (atomic_flag_test_and_set(spinlock)) { // 忙等待 }atomic_flag_test_and_set如果标志位为false则设置为true并返回false表示获取成功如果为true则返回true表示获取失败。释放锁atomic_flag_clear(spinlock);将标志位设为false。自旋锁的优缺点优点低延迟不会让线程休眠避免了线程切换的开销。减少系统调度开销等待锁的线程不会被阻塞不需要上下文切换。缺点CPU 资源浪费如果锁持有时间较长等待线程会一直自旋浪费 CPU。可能引起活锁多个线程同时自旋如果没有退避策略可能都无法进入临界区。使用场景短暂等待锁被占用的时间很短比如简单的计数器操作。多线程锁使用通常用于系统底层同步多个 CPU 对共享资源的访问。不可睡眠的场景比如中断处理程序中不能休眠只能用自旋锁。Linux 提供的自旋锁系统调用#include pthread.h int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); int pthread_spin_unlock(pthread_spinlock_t *lock); int pthread_spin_init(pthread_spinlock_t *lock, int pshared); int pthread_spin_destroy(pthread_spinlock_t *lock);psharedPTHREAD_PROCESS_PRIVATE表示线程间共享PTHREAD_PROCESS_SHARED表示进程间共享。互斥锁 vs 自旋锁 vs 读写锁锁类型行为适用场景缺点互斥锁Mutex获取失败时休眠释放时唤醒锁持有时间较长竞争不激烈上下文切换开销自旋锁Spinlock获取失败时忙等待锁持有时间极短多核环境CPU 浪费可能活锁读写锁Rwlock读共享写独占多读少写可能读者/写者饥饿选择原则如果临界区执行时间短且 CPU 多核用自旋锁。如果临界区执行时间长或者单核用互斥锁。如果读多写少用读写锁。读者写者问题的变种读者优先只要有一个读者在读后续读者直接进入写者等待。写者优先一旦有写者等待后续读者阻塞写者优先进入。公平竞争读者和写者按到达顺序竞争避免饥饿。读写锁的实现细节读写锁的实现通常需要一个计数器和两个条件变量或信号量reader_count当前读者数量。writer_count当前写者数量通常为 0 或 1。mutex保护计数器。read_cond读者等待的条件变量。write_cond写者等待的条件变量。读者进入时如果写者正在写等待。否则reader_count进入读。写者进入时如果读者正在读或写者正在写等待。否则进入写。自旋锁的优化退避策略自旋失败后暂停一段时间再重试减少 CPU 浪费。队列自旋锁每个线程在一个队列中等待避免所有线程同时自旋。自适应自旋锁根据历史成功率动态调整自旋时间。实际应用中的建议避免锁的嵌套容易导致死锁。锁的粒度要小只保护必要的临界区。读写锁并不是解决所有并发问题的万能工具如果写操作也很频繁读写锁可能不如互斥锁。自旋锁不要用于单核单核自旋没有意义因为自旋的线程占着 CPU持有锁的线程无法运行。

相关新闻

2026学年陇东学院——科技创新协会招新反馈

2026学年陇东学院——科技创新协会招新反馈

9月12日至13日,陇东学院科技创新协会顺利开展为期两天的线下招新活动。本次招新工作有序推进,共吸纳新成员450人。协会下设电子部、电控部、设计部、视觉部、文创部五大部门,面向不同兴趣方向的新生,提供多元的实践学习平台。招新…

2026/10/2 21:10:23 阅读更多 →
Product Hunt 每日热榜 | 2026-10-01

Product Hunt 每日热榜 | 2026-10-01

1. Pexo 标语:制作出精准完美的发布视频,掌控每一个细节。 介绍:Pexo 让制作启动视频变得像聊天一样简单。只需分享你的产品、网站或素材,它就会帮你规划故事情节,选择合适的人工智能模型来完成各项任务,…

2026/10/2 21:10:23 阅读更多 →
Chatto事件溯源架构揭秘:为什么用NATS JetStream替代关系型数据库

Chatto事件溯源架构揭秘:为什么用NATS JetStream替代关系型数据库

Chatto事件溯源架构揭秘:为什么用NATS JetStream替代关系型数据库 【免费下载链接】chatto A fully-featured team and group chat application that you can easily selfhost. 项目地址: https://gitcode.com/gh_mirrors/chatt/chatto Chatto 是一款功能完整…

2026/10/2 21:10:23 阅读更多 →

最新新闻

Cocos Creator做猜成语游戏:轻量跨端2D开发实战

Cocos Creator做猜成语游戏:轻量跨端2D开发实战

简介:本资源是一套基于Cocos Creator 2.3.3开发的完整猜成语游戏项目源码,面向游戏开发初学者与Unity/Cocos转型开发者,解决2D交互类益智游戏从零搭建、UI逻辑联动、本地数据驱动及跨平台发布等核心实践问题。压缩包共2765个文件,…

2026/10/2 21:45:52 阅读更多 →
yolov5人群计数与阈值报警:从Coco预训练到工程落地的完整方案

yolov5人群计数与阈值报警:从Coco预训练到工程落地的完整方案

简介:面向计算机视觉与公共安防场景,这份资源提供基于YOLOv5的实时人群计数与阈值报警实现。采用COCO预训练的person类权重,可对室内外不严重拥堵的画面进行人数统计,并在超过设定阈值时触发报警,适合需要快速部署人群…

2026/10/2 21:45:52 阅读更多 →
DOSBox中DEBUG汇编实战:从数制转换到物理地址计算

DOSBox中DEBUG汇编实战:从数制转换到物理地址计算

简介:本资源是《IBM-PC汇编语言程序设计》(沈美明、温冬婵编著)配套习题的完整参考答案,面向计算机专业本科生、汇编语言初学者及备考人员,旨在解决课后练习无标准解析、核心概念理解困难、手算验证缺乏依据等典型学习…

2026/10/2 21:45:52 阅读更多 →
北邮研一数据库大作业:学生成绩管理系统从选题到验收完整路径

北邮研一数据库大作业:学生成绩管理系统从选题到验收完整路径

简介:这份资源是北邮研一数据库课程大作业的完整详解文档,面向正在修读数据库系统课程、需要完成课程设计的研究生及高年级本科生。内容围绕学生成绩管理系统展开,覆盖需求分析、数据库设计、ER图绘制、逻辑结构设计与建表程序等完整流程&…

2026/10/2 21:45:51 阅读更多 →
校园二手交易网站毕设项目:SSM+Vue全链路实战解析

校园二手交易网站毕设项目:SSM+Vue全链路实战解析

简介:一套完整的校园二手交易网站设计与实现项目包,面向计算机相关专业毕业设计或课程设计使用者,采用 Java SSM Vue 技术栈构建,覆盖前台用户和后台管理员两类角色。系统包含商品浏览、二手发布、收藏下单、订单发货、求购信息…

2026/10/2 21:44:51 阅读更多 →
南山家常客家风味粤菜靠谱商家测评排名

南山家常客家风味粤菜靠谱商家测评排名

行业选择四大踩坑难题在深圳南山桃源街道周边找家常客家风味粤菜,很多人都踩过不少坑。 一是找不着地道口味的店:想吃正宗的手撕盐焗鸡、客家酿豆腐,跑了好几家店要么味道不够醇厚,要么用的食材不新鲜,吃不出老家的烟火…

2026/10/2 21:44:51 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →