Linux 线程同步:读写锁

发布时间:2026/10/3 22:30:57

Linux 线程同步:读写锁 Linux 线程同步读写锁课程尚硅谷《嵌入式 Linux 应用层开发》第 4 章线程处理依据2026-09-29 19:30 录音转写整理到15:02课程 PDF 第 150—158 页。本节边界讲清读写锁原理、基础 API、未加写锁的错误示例、加写锁后的修正以及读写线程执行顺序实验的准备。第 158 页开始的“写饥饿”对应录音 17:56 以后留到下一节。1. 为什么互斥锁之后还要学习读写锁互斥锁不区分读和写。只要一个线程持有互斥锁其他线程都要等待。但很多共享数据具有“读得多、写得少”的特点多个线程只读取同一份稳定数据时可以并发执行写线程修改数据时必须独占写入期间不能让读线程看到一半旧、一半新的状态。读写锁read-write lock用两种加锁方式表达这种规则读锁共享占用允许多个读者并发 写锁独占占用只允许一个写者进入2. 读锁与写锁的兼容关系当前状态新读者申请读锁新写者申请写锁没有线程持锁可以获得可以获得一个或多个线程持有读锁通常可继续获得读锁必须等待全部读锁释放一个线程持有写锁必须等待必须等待“通常可继续获得读锁”还受调度策略以及是否已有写者等待等因素影响不能据此假定读者永远优先。读否是写否是线程申请访问共享数据读还是写当前是否有写者持锁取得读锁阻塞等待读取共享数据释放读锁是否有任何读者或写者持锁取得写锁修改共享数据释放写锁3. “多个线程同时读没问题”的适用条件录音用“大家一起看已经写好的黑板”解释读锁。这个比喻成立需要满足读者只读取不修改对象在读取期间仍然存在没有写者绕过锁修改所有读者和写者使用同一把读写锁。如果共享数据可能被写线程修改读线程也必须获取读锁。否则读线程可能与写线程形成数据竞争或看到不一致的组合状态。4.pthread_rwlock_t的使用原则读写锁类型声明为#includepthread.hpthread_rwlock_trwlock;教材第 151 页展示了 glibc 内部的联合体定义。应用程序应把pthread_rwlock_t当作不透明类型只通过pthread.h使用不访问内部成员不复制读写锁对象给相关函数传入同一个对象的地址。5. 初始化与销毁5.1 静态初始化默认属性下可以在定义时初始化staticpthread_rwlock_trwlockPTHREAD_RWLOCK_INITIALIZER;这种方式不再调用pthread_rwlock_init()。5.2 动态初始化pthread_rwlock_trwlock;interrorpthread_rwlock_init(rwlock,NULL);第二个参数是属性对象。传NULL表示使用默认属性。5.3 销毁interrorpthread_rwlock_destroy(rwlock);销毁前必须确认所有使用它的线程已经结束或不再访问它当前没有读者或写者持锁不会再有线程等待这把锁。静态初始化只表示不需要调用pthread_rwlock_init()。如果锁与进程同寿命程序退出时系统会回收进程资源如果对象有独立、可重复的生命周期则应在安全时显式销毁。6. 本节五个核心 APIintpthread_rwlock_init(pthread_rwlock_t*restrict rwlock,constpthread_rwlockattr_t*restrict attr);intpthread_rwlock_rdlock(pthread_rwlock_t*rwlock);intpthread_rwlock_wrlock(pthread_rwlock_t*rwlock);intpthread_rwlock_unlock(pthread_rwlock_t*rwlock);intpthread_rwlock_destroy(pthread_rwlock_t*rwlock);函数作用获取不到时pthread_rwlock_rdlock获取读锁有写者阻挡时等待pthread_rwlock_wrlock获取写锁只要还有读者或写者就等待pthread_rwlock_unlock释放当前线程持有的一次读锁或写锁不适用同一把读写锁的读锁和写锁都用pthread_rwlock_unlock()释放。Pthreads 函数成功返回0否则返回错误号。基础检查写法interrorpthread_rwlock_rdlock(rwlock);if(error!0){fprintf(stderr,pthread_rwlock_rdlock: %s\n,strerror(error));}7. 第一个实验写操作没有加锁教材第 152—154 页故意让两个写线程修改同一个变量却不给写操作加锁staticintshared_data0;staticvoid*writer_without_lock(void*arg){inttempshared_data1;sleep(1);shared_datatemp;printf(%s 完成写入shared_data %d\n,(char*)arg,shared_data);returnNULL;}sleep(1)是为了放大两个写线程发生交错的机会步骤写线程 1写线程 2shared_data1读取 0算出temp102睡眠读取 0算出temp103睡眠04写回 115写回 11预期加两次得到 2实际可能得到 1。这与上一节number的丢失更新本质相同。7.1 只有读线程加读锁为什么仍然错误教材错误示例中的读线程这样写pthread_rwlock_rdlock(rwlock);printf(shared_data %d\n,shared_data);pthread_rwlock_unlock(rwlock);读线程之间可以互相协调但两个写线程完全没有使用这把锁。读写锁只有在所有访问者共同遵守时才有效。读者使用 rwlock 写者绕过 rwlock ↓ 同步协议不完整8. 修正实验写线程获取写锁教材第 154—156 页给写操作补上写锁staticvoid*lock_writer(void*arg){pthread_rwlock_wrlock(rwlock);inttempshared_data1;sleep(1);/* 仅为演示持锁期间其他线程会等待 */shared_datatemp;printf(%s 完成写入shared_data %d\n,(char*)arg,shared_data);pthread_rwlock_unlock(rwlock);returnNULL;}执行过程变成写线程 2读写锁写线程 1写线程 2读写锁写线程 1阻塞等待wrlock成功wrlockshared_data 由 0 变 1unlock获得写锁shared_data 由 1 变 2unlock两个写线程不再同时进入临界区最终值稳定为 2。9. 基础完整练习两个写者和三个读者下面的例子保留录音中的主要现象同时补上返回值检查。读者看到 0、1 或 2 都有可能因为调度顺序不确定但每次读取都发生在读锁保护下最终值应为 2。#includepthread.h#includestdio.h#includestdlib.h#includestring.h#includeunistd.hstaticintshared_data0;staticpthread_rwlock_trwlockPTHREAD_RWLOCK_INITIALIZER;staticvoidcheck_pthread(interror,constchar*operation){if(error!0){fprintf(stderr,%s: %s\n,operation,strerror(error));exit(EXIT_FAILURE);}}staticvoid*reader(void*arg){constchar*namearg;check_pthread(pthread_rwlock_rdlock(rwlock),pthread_rwlock_rdlock);printf(%s 读取%d\n,name,shared_data);sleep(1);/* 演示多个读者可以同时持有读锁 */check_pthread(pthread_rwlock_unlock(rwlock),pthread_rwlock_unlock);returnNULL;}staticvoid*writer(void*arg){constchar*namearg;check_pthread(pthread_rwlock_wrlock(rwlock),pthread_rwlock_wrlock);shared_data;printf(%s 写入%d\n,name,shared_data);check_pthread(pthread_rwlock_unlock(rwlock),pthread_rwlock_unlock);returnNULL;}intmain(void){pthread_tthreads[5];check_pthread(pthread_create(threads[0],NULL,reader,reader1),pthread_create);check_pthread(pthread_create(threads[1],NULL,reader,reader2),pthread_create);check_pthread(pthread_create(threads[2],NULL,writer,writer1),pthread_create);check_pthread(pthread_create(threads[3],NULL,reader,reader3),pthread_create);check_pthread(pthread_create(threads[4],NULL,writer,writer2),pthread_create);for(inti0;i5;i){check_pthread(pthread_join(threads[i],NULL),pthread_join);}printf(最终值%d\n,shared_data);check_pthread(pthread_rwlock_destroy(rwlock),pthread_rwlock_destroy);returnEXIT_SUCCESS;}编译运行gcc-Wall-Wextra-pthreadrwlock_basic.c-orwlock_basic ./rwlock_basic9.1 观察重点多运行几次输出顺序可能改变先创建的线程不保证先执行多个读者可以同时持有读锁写者必须等所有当前读者释放写者持锁时新的读者和其他写者都不能进入所有线程结束后最终值为 2。10. 为什么创建顺序不等于执行顺序教材第 156—158 页把第二个写线程放到多个读线程之间创建用来观察读写顺序创建 writer1 创建 reader1、reader2、reader3 创建 writer2 创建 reader4、reader5、reader6这只是向系统提交线程的先后顺序。线程何时真正获得 CPU、何时开始申请锁由调度器和当时的锁状态共同决定。因此不能写出这样的业务假设因为 reader1 先 pthread_create所以它一定先于 writer2 读取如果业务需要固定顺序应使用条件变量、信号量、队列或明确的join关系不能依赖创建顺序和sleep()。11.sleep()在录音示例中的作用录音使用sleep()有两个教学目的放大未加锁写操作的交错更容易观察丢失更新延长持有读锁或写锁的时间更容易看清其他线程是否被阻塞。sleep()不能提供同步保证。机器负载、调度策略和运行环境改变后线程顺序仍可能变化。12. 什么时候读写锁比互斥锁合适读写锁更适合共享数据读多写少一次读取工作并非极短多个读者并发确实能提高吞吐量能接受更复杂的锁规则和潜在调度问题。普通互斥锁可能更合适写操作频繁临界区很短并发读带来的收益很小更重视代码简单和容易验证。读写锁并不天然比互斥锁快应通过实际负载测量。13. 常见错误错误后果正确做法读者加锁写者不加锁同步协议失效仍有数据竞争所有读写者使用同一把锁写线程误用rdlock多个写者可能同时进入修改共享状态必须用wrlock读取可变数据时完全不加锁可能看到不一致状态获取读锁后再读取忘记unlock后续线程长期阻塞每条退出路径都释放锁未持锁却调用unlock未定义行为只释放当前线程实际持有的锁锁仍被持有时销毁未定义行为所有线程结束并释放后销毁用sleep规定执行顺序结果随调度变化使用正式同步机制认为先创建必然先执行业务顺序不可靠显式表达依赖关系在读锁中修改共享数据破坏多个读者并发的前提修改时获取写锁14. 录音与教材表述校正转写中的“图形锁”“图像处理”在本节多数应为“读写锁”。读者能够并发不表示读共享内存永远安全如果存在写者读者也必须参与同步。pthread_rwlock_t的内部结构属于具体实现应用代码不应依赖其字段。动态初始化必须传两个参数pthread_rwlock_init(rwlock, NULL)。静态初始化使用PTHREAD_RWLOCK_INITIALIZER不能再对同一对象重复初始化。读锁和写锁都通过同一个pthread_rwlock_unlock()释放。两个未加锁写线程得到 1 是教学中常见现象但数据竞争属于未定义行为不能保证每次都得到同样结果。创建写线程后调用sleep(3)只能增加随后读到最终值的概率不能构成严格同步。读写锁释放后究竟由读者还是写者先获得不能依赖普通程序中的固定猜测。15. 本节最低掌握标准学完后应能回答读锁和写锁分别允许哪些线程并发为什么读线程也要加锁为什么写线程必须使用pthread_rwlock_wrlock()静态初始化和动态初始化怎样写读锁和写锁分别怎样释放为什么pthread_create()的先后顺序不等于实际执行顺序sleep()为什么不能替代同步机制什么情况下读写锁可能比互斥锁更合适最低实践要求独立写出一个读线程和一个写线程正确使用rdlock → 读取 → unlock正确使用wrlock → 修改 → unlock等待所有线程结束后再销毁读写锁能解释未加写锁时发生的丢失更新。16. PDF 页码与录音时间索引主题PDF 页码录音时间读写锁工作原理15000:05—01:00类型、初始化与销毁15101:00—04:23rdlock、wrlock、unlock151—15202:00—04:23未加写锁的实验152—15404:24—11:45给写操作添加写锁154—15611:46—13:55读写操作顺序实验准备156—15813:56—15:02写饥饿158 页起本次截止点之后下一节学习进一步核对资料POSIX Programmer’s Manual读写锁初始化与销毁POSIX Programmer’s Manual获取读锁POSIX Programmer’s Manual获取写锁POSIX Programmer’s Manual释放读写锁17. 下一节预习边界第 158 页后半开始讨论写饥饿读者持续到来时等待中的写者可能长时间无法获得写锁。这个主题对应录音 17:56 以后不属于本次 15:02 前的学习范围。继续学习时重点关注为什么已有写者等待时新读者是否还能进入会影响公平性Linux 中读写锁属性怎样改变读写优先策略为什么“读者并发越多越好”并不总是成立。
延伸阅读

更多相关文章

2026/10/3 22:30:57

DiPlay 实测:iPhone 绕过硬件盒子直连 BYD 车机的思路拆解

先说清楚这个项目解决的是什么问题 原厂不支持 CarPlay 的车型,想用 iPhone 投屏,市面上最常见的方案是买一个第三方盒子:盒子插在车机 USB 上,伪装成一个 CarPlay 接收端,iPhone 再通过蓝牙或 Wi-Fi 连到盒子上。这套…

2026/10/3 23:21:00

PostgreSQL空间排查指南:从表大小到WAL与死元组

某天凌晨,监控告警把值班手机震到发烫:磁盘使用率飙到93%,业务日志里全是“could not extend file”的报错。第一反应是赶紧找出哪张表在疯涨,但用psql敲了几条SQL之后发现,统计出来的库大小加起来只有磁盘占用的一半不…

2026/10/3 23:20:59

QGIS快速标注按钮:从字段选择到出图全流程解析

做GIS的应该都有过这种经历:领导说“把图斑名字标出来”,常规操作是先打开图层属性,翻到“标注”选项卡,勾上“标注该图层”,再选字段、调字体、调位置,一套流程下来时间没少花。后来我用QGIS时&#xff0c…

2026/10/3 23:20:59

Bibliometrix安装配置全攻略:从R环境搭建到Biblioshiny可视化分析

第一次用Bibliometrix做文献计量分析的时候,我差点被安装这关劝退。倒不是这个R包本身多难装,而是网上教程大多只丢一句install.packages("bibliometrix"),然后就默认你能跑通。真到自己动手,R版本不匹配、依赖包编译失…

2026/10/3 23:20:59

Android Intent传值避坑指南:正确获取参数的5种方式与常见问题

避坑指南:正确获取Intent传递的值,这几种方式我全给你捋明白了 做Android开发,谁还没跟Intent打过交道?启动Activity、传参数、接收返回值、处理外部链接调起,几乎每个页面跳转背后都有Intent在默默干活。我早期写项目…

2026/10/3 23:15:59

JavaScript数组方法实战:从map到reduce的高频用法与易错点

每个人的备赛与参赛经验,就是在这些最基本的知识点上拉开了差距。我给自己定的目标是:每个高频方法不假思索就能写对。 2. 遍历、筛选、映射:比赛中出场率最高的三个方法 2.1 map:数据清洗和列表渲染的头号工具 map的作用一句话…

2026/10/2 8:16:46

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/10/2 18:20:53

如何划分训练/验证集: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/3 15:02:19

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

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

2026/10/3 0:04:31

国内大学生必备的AI写作辅助软件是哪款?

国内高校学生在论文写作过程中,越来越依赖AI辅助工具提升效率,主流方案以本土化全流程工具为核心,结合通用大模型与专业插件,覆盖选题构思、框架搭建、初稿撰写、查重降重、格式调整等关键环节,本文将深入解析当前主流…

2026/10/3 0:04:31

Codex接入Jev模型完整指南:配置方法、本地部署与踩坑排查

最近不少人在讨论 Codex 搭配 Jev 这套玩法,我一开始没太当回事,直到自己把 Jev 接进 Codex跑了几轮编码任务之后,才明白那些说“直接起飞”的人是怎么想的。Codex 作为工具本身已经够能打了,但模型固定、上下文策略固定&#xff…

2026/10/3 0:04:31

GitHub 热门: NVIDIA/Model-Optimizer

👋 Hi,我擅长 AI 大模型应用落地、意识解码与 AI 开发工具链 。 💡 创业路上,用技术换时间,一起把 AI 变成生产力 🚀 >GitHub 热门: NVIDIA/Model-Optimizer 凌晨两点,你刚把跑通了的 Qwen3.…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑