news 2026/4/26 13:39:04

linux学习进程 线程同步——读写锁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
linux学习进程 线程同步——读写锁

在Linux线程编程中,我们已经学习了互斥锁(mutex),它能有效解决线程间的竞争问题,但互斥锁存在一个局限性:无论线程是读取资源还是修改资源,都会独占锁,导致读取操作之间也会相互阻塞,降低程序的并发效率。而读写锁(Read-Write Lock)正是为了解决这一问题而生,它区分了“读取操作”和“写入操作”,实现了“读共享、写独占”的机制,大幅提升了读多写少场景下的程序性能。

一、读写锁的核心概念

读写锁,也叫共享-独占锁(Shared-Exclusive Lock),核心逻辑是:

多个线程可以同时持有读锁(共享锁),此时线程只能对资源执行读取操作,不能修改;

只有一个线程可以持有写锁(独占锁),此时其他线程(无论是读还是写)都无法获取任何锁,只能阻塞等待;

读锁和写锁不能同时存在,写锁优先级通常高于读锁(避免写操作长期被读操作阻塞,不同系统可能有差异)。

简单来说:读可以共享,写必须独占。这一特性使其特别适合“读多写少”的场景,比如日志读取、配置文件读取、数据库查询等,既能保证数据一致性,又能最大化并发效率。

二、读写锁与互斥锁的区别

为了更清晰理解读写锁的优势,我们对比一下它与互斥锁的核心差异:

特性

互斥锁(mutex)

读写锁(rwlock)

锁类型

单一独占锁

读锁(共享)、写锁(独占)

并发能力

低,同一时刻只能有一个线程持有锁

高,读操作可并发,仅写操作独占

适用场景

读少写多、读写频率相近

读多写少

阻塞情况

读-读、读-写、写-写均阻塞

读-读不阻塞,读-写、写-写阻塞

三、读写锁的常用API(Linux系统)

Linux中读写锁的相关操作都定义在<pthread.h>头文件中,核心API分为4类:初始化、加锁、解锁、销毁。需要注意的是,读写锁的变量类型为pthread_rwlock_t

1. 初始化读写锁

有两种初始化方式:静态初始化和动态初始化。

// 1. 静态初始化(推荐,简单高效) pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 2. 动态初始化(需手动销毁) int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);

参数说明:

rwlock:指向读写锁变量的指针;

attr:读写锁的属性,通常设为NULL(使用默认属性);

返回值:成功返回0,失败返回非0错误码。

2. 加锁操作(核心)

读写锁有两种加锁方式,分别对应读操作和写操作,还有非阻塞版本(避免线程长期阻塞)。

// 加读锁(共享锁) int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 加写锁(独占锁) int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 非阻塞加读锁(尝试加锁,失败立即返回,不阻塞) int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 非阻塞加写锁 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

说明:

阻塞版本(rdlock/wrlock):如果锁被占用,线程会阻塞,直到获取到锁;

非阻塞版本(tryrdlock/trywrlock):如果锁被占用,不会阻塞,直接返回错误码(EBUSY),适合不需要等待的场景;

返回值:成功返回0,失败返回非0错误码。

3. 解锁操作

无论加的是读锁还是写锁,都使用同一个解锁函数,系统会自动区分锁类型。

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

注意:必须由持有锁的线程解锁,否则会导致未定义行为(比如程序崩溃)。

4. 销毁读写锁

仅动态初始化的读写锁需要手动销毁,静态初始化的无需销毁(系统自动回收)。

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

说明:销毁前,必须确保所有线程都已释放锁,否则会返回错误。

四、实战案例:用读写锁实现“读多写少”场景

我们通过一个简单的案例,演示读写锁的使用:假设有一个共享变量(模拟数据),多个读线程读取该变量,1个写线程修改该变量,用读写锁保证数据一致性,同时提升读操作的并发效率。

案例代码

#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> // 共享变量(模拟数据) int shared_data = 100; // 读写锁 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; // 读线程函数:读取共享变量 void *read_thread(void *arg) { int tid = *(int *)arg; while (1) { // 加读锁 pthread_rwlock_rdlock(&rwlock); printf("读线程%d:读取到数据 = %d\n", tid, shared_data); // 解锁 pthread_rwlock_unlock(&rwlock); // 模拟读操作耗时 sleep(1); } return NULL; } // 写线程函数:修改共享变量 void *write_thread(void *arg) { int tid = *(int *)arg; while (1) { // 加写锁 pthread_rwlock_wrlock(&rwlock); // 修改共享变量 shared_data++; printf("=== 写线程%d:修改数据为 = %d ===\n", tid, shared_data); // 解锁 pthread_rwlock_unlock(&rwlock); // 模拟写操作耗时(写频率低于读) sleep(3); } return NULL; } int main() { pthread_t read_tids[3], write_tid; int tids[3] = {1, 2, 3}; int i; // 创建3个读线程 for (i = 0; i < 3; i++) { pthread_create(&read_tids[i], NULL, read_thread, &tids[i]); } // 创建1个写线程 pthread_create(&write_tid, NULL, write_thread, &i); // 等待线程结束(实际中不会退出,这里仅作演示) for (i = 0; i < 3; i++) { pthread_join(read_tids[i], NULL); } pthread_join(write_tid, NULL); // 销毁读写锁(静态初始化可省略,但写了也没错) pthread_rwlock_destroy(&rwlock); return 0; }

编译与运行

编译时需要链接 pthread 库(Linux下线程库默认不自动链接):

gcc rwlock_demo.c -o rwlock_demo -lpthread ./rwlock_demo

运行结果分析

从运行结果中可以观察到两个关键现象:

  1. 3个读线程可以同时读取数据(输出连续的“读线程”信息),不会相互阻塞;

  2. 当写线程开始修改数据时,所有读线程都会阻塞,直到写线程解锁,之后读线程才能继续读取(写操作独占锁);

  3. 写操作频率低于读操作,充分体现了读写锁“读共享”的优势,提升了并发效率。

五、读写锁的注意事项(避坑重点)

  1. 锁的顺序问题:同一线程中,不能先加写锁再加读锁(会导致死锁);可以先加读锁,再尝试加写锁,但此时会阻塞(因为读锁和写锁不能共存)。

  2. 写锁优先级:Linux默认写锁优先级高于读锁,即当有写线程等待时,新的读线程会被阻塞,优先让写线程获取锁,避免写操作“饥饿”(长期得不到执行)。

  3. 非阻塞锁的使用:非阻塞版本(tryrdlock/trywrlock)返回EBUSY时,不要直接死循环重试,建议适当延时后再尝试,避免占用过多CPU资源。

  4. 资源释放:动态初始化的读写锁,必须在所有线程结束后销毁;如果线程异常退出,要确保锁被释放,否则会导致其他线程永久阻塞。

  5. 适用场景:读写锁只适合“读多写少”的场景,如果写操作频繁,使用读写锁反而会因为锁切换带来额外开销,此时不如使用互斥锁更高效。

六、总结

读写锁是Linux线程同步中一种高效的同步机制,核心是“读共享、写独占”,解决了互斥锁在“读多写少”场景下并发效率低的问题。

核心要点回顾:

读写锁分为读锁(共享)和写锁(独占),读-读不阻塞,读-写、写-写阻塞;

常用API:初始化(init)、加读锁(rdlock)、加写锁(wrlock)、解锁(unlock)、销毁(destroy);

适合读多写少场景,写频繁场景建议用互斥锁;

注意避免死锁、确保锁的正确释放,关注写锁优先级问题。

下一节,我们将学习线程同步的其他机制(如条件变量),进一步完善线程同步的知识体系。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 13:38:21

如何5秒内获取百度网盘提取码:免费开源工具的终极解决方案

如何5秒内获取百度网盘提取码&#xff1a;免费开源工具的终极解决方案 【免费下载链接】baidupankey 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 还在为百度网盘提取码而烦恼吗&#xff1f;每次找到心仪的资源&#xff0c;却卡在提取码这一步&#xff0…

作者头像 李华
网站建设 2026/4/26 13:33:24

AI智能体Riona:基于ReAct框架的自主规划与执行实践

1. 项目概述&#xff1a;一个名为Riona的AI智能体最近在GitHub上看到一个挺有意思的项目&#xff0c;叫“Riona-AI-Agent”。光看名字&#xff0c;你可能会觉得这又是一个基于大语言模型&#xff08;LLM&#xff09;的聊天机器人或者简单的问答助手。但当我深入进去&#xff0c…

作者头像 李华
网站建设 2026/4/26 13:29:09

零基础快速上手:Kohya_SS AI模型训练终极指南 [特殊字符]

零基础快速上手&#xff1a;Kohya_SS AI模型训练终极指南 &#x1f680; 【免费下载链接】kohya_ss 项目地址: https://gitcode.com/GitHub_Trending/ko/kohya_ss 还在为复杂的AI模型训练环境配置而头疼吗&#xff1f;Kohya_SS作为目前最受欢迎的Stable Diffusion训练工…

作者头像 李华