系统编程-读写锁

1|0读写锁

  1. 互斥锁的缺陷

    • 互斥锁无论读取共享资源,还是修改共享资源,都是要上锁,而且在上锁期间,其它线程不能上锁
  2. 概念

    • 与互斥锁类似,但是读写锁允许更高的并行性。特性是,写独占,读共享
  3. 读写锁的状态

    • 特别强调:读写锁只有一把,但具有两种状态
    • 读模式下的加锁状态(读锁)
    • 写模式下的加锁状态(写锁)
  4. 读写锁的特性

    • 读写锁是“写锁”时,解锁前,所有对该锁该锁的线程都阻塞
    • 读写锁是“读锁”时,如果线程以读模式对其加锁会成功;写模式加锁就会阻塞
    • 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,是以独占模式锁住的
    • 写独占,读共享
    • 读写锁非常适合对于数据结构读的次数远大于写的情况
    • 写锁优先级高
  5. 读写锁函数的接口

    • 定义一个读写锁变量----->pthread_rwlock_t rwlock

    • 初始化读写锁变量----->pthread_rwlock_init()

    #include <pthread.h> int pthread_rwlock_init( pthread_rwlock_t *restrict rwlock, //参数1:读写锁变量的地址 const pthread_rwlockattr_t *restrict attr //参数2:属性,一般是NULL ); // 返回值:成功0,失败非0错误码
    • 读锁上锁----->pthread_rwlock_rdlock()
    #include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 参数:读写锁变量的地址 // 返回值:成功0,失败非0错误码
    • 写锁上锁----->pthread_rwlock_wrlock()
    #include <pthread.h> int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 参数:读写锁变量的地址 // 返回值:成功0,失败非0错误码
    • 读写锁解锁---->pthread_rwlock_unlock
    #include <pthread.h> int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 参数:读写锁变量的地址 // 返回值:成功0,失败非0错误码
    • 销毁读写锁---->pthread_rwlock_destroy
    #include <pthread.h> int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); // 参数:读写锁变量的地址 // 返回值:成功0,失败非0错误码
    • 案例:
    #include <stdio.h> #include <pthread.h> #include <unistd.h> // 读写锁变量 pthread_rwlock_t rwlock; // 全局变量 int main_val = 0; void *routine1(void *arg) { //写锁 pthread_rwlock_wrlock(&rwlock); //写操作 main_val=100; for(int i=0;i<5;i++) { sleep(1); main_val+=main_val*i; printf("%d routine1 100 main_val:%d\n",i,main_val); } //解锁 pthread_rwlock_unlock(&rwlock); //退出 pthread_exit(NULL); } void *routine2(void *arg) { //写锁 pthread_rwlock_wrlock(&rwlock); //写操作 main_val=200; for(int i=0;i<5;i++) { sleep(1); main_val+=main_val*i; printf("%d routine2 200 main_val:%d\n",i,main_val); } //解锁 pthread_rwlock_unlock(&rwlock); //退出 pthread_exit(NULL); } void *routine3(void *arg) { sleep(1); //读锁 pthread_rwlock_rdlock(&rwlock); //读操作 for(int i=0;i<5;i++) { sleep(1); printf("routine3 main_val:%d\n",main_val); } //解锁 pthread_rwlock_unlock(&rwlock); //退出 pthread_exit(NULL); } void *routine4(void *arg) { sleep(1); //读锁 pthread_rwlock_rdlock(&rwlock); //读操作 for(int i=0;i<5;i++) { sleep(1); printf("routine4 main_val:%d\n",main_val); } //解锁 pthread_rwlock_unlock(&rwlock); //退出 pthread_exit(NULL); } int main(int argc, char const *argv[]) { // 初始化读写锁 pthread_rwlock_init(&rwlock, NULL); // 线程ID数组 pthread_t tid[4]; // 线程函数指针数组 void* (*fp[4])(void*) = {routine1, routine2, routine3, routine4}; // 创建线程 for (int i = 0; i < 4; ++i) { pthread_create(&tid[i], NULL, fp[i], NULL); } // 等待线程结束回收 for (int i = 0; i < 4; ++i) { pthread_join(tid[i], NULL); } // 销毁读写锁 pthread_rwlock_destroy(&rwlock); return 0; }

2|0条件变量

  1. 什么是条件变量

    • 线程因为某一条件/情况不成立,进入一个变量中等待,这个存放线程的变量的就是条件变量。条件变量本身不是锁,但它可以造成线程堵塞。通常是与互斥锁配合使用。给多线程提供一个会和的场合
  2. 关于条件变量的函数接口

    • 定义一个条件变量----->pthread_cond_t cond

    • 初始化条件变量----->pthread_cond_init()

    #include <pthread.h> int pthread_cond_init( pthread_cond_t *cond, // 参数1:条件变量地址 pthread_condattr_t *cond_attr // 参数2:普通属性,NULL ); // 返回值:成功0,失败非0错误码
    • 如何进入条件变量等待
    #include <pthread.h> int pthread_cond_wait( pthread_cond_t *cond, pthread_mutex_t *mutex ); int pthread_cond_timedwait( pthread_cond_t *cond, // 参数1:条件变量的地址 pthread_mutex_t *mutex, // 参数2:互斥锁的地址---->进入条件变量会自动解锁 const struct timespec *abstime // 参数3: 绝对时间 ); // 返回值:成功0,失败非0错误码
    • 关于pthread_cond_timedwait
    struct timespec { time_t tv_sec; // 秒 long tv_nsec; // 纳秒 }

    获取当前时间:

    time_t cur=time(NULL); struct timespec t; t.tvsec=cur+1; pthread_cond_timedwait(&cond, &mutex, &t);
    • 如何唤醒条件变量中等待的线程?---->线程离开条件变量会自动上锁
    #include <pthread.h> // 单播:随机唤醒(至少)一个在条件变量的线程 int pthread_cond_signal(pthread_cond_t *cond); // 参数: 条件变量的地址 // 唤醒所有在条件变量中等待的线程 int pthread_cond_broadcast(pthread_cond_t *cond); // 参数: 条件变量的地址 // 返回值:成功0,失败非0错误码
    • 销毁条件变量------>pthread_cond_destroy()
    #include <pthread.h> int pthread_cond_destroy(pthread_cond_t *cond); // 参数:条件变量的地址 // 返回值:成功0,失败非0错误码

    -案例:
    练习:有4个小孩,每个小孩的任务就是领取生活费1000,他们回学校之前银行卡父亲先打个两千,2个小孩可以领取到,就是两个线程退出,另外两个进入条件变量等待,父亲再打钱1000,唤醒所有的小孩来拿钱,过了一会,再打1000,再唤醒最后一个小孩起来拿钱赶紧去上学。

    #include <stdio.h> #include <pthread.h> #include <unistd.h> // 互斥锁变量 pthread_mutex_t mutex; // 条件变量 pthread_cond_t cond; int money = 2000; void* func1(void*arg) { pthread_mutex_lock(&mutex); if(money < 1000) { pthread_cond_wait(&cond, &mutex); } money -= 1000; printf("boy1 拿到钱了\n"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } void* func2(void*arg) { pthread_mutex_lock(&mutex); if(money < 1000) { pthread_cond_wait(&cond, &mutex); } money -= 1000; printf("boy2 拿到钱了\n"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } void* func3(void*arg) { pthread_mutex_lock(&mutex); if(money < 1000) { pthread_cond_wait(&cond, &mutex); } money -= 1000; printf("boy3 拿到钱了\n"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } void* func4(void*arg) { pthread_mutex_lock(&mutex); if(money < 1000) { pthread_cond_wait(&cond, &mutex); } money -= 1000; printf("boy4 拿到钱了\n"); pthread_mutex_unlock(&mutex); pthread_exit(NULL); } int main(int argc, char const *argv[]) { // 初始化互斥锁 pthread_mutex_init(&mutex, NULL); // 初始化条件变量 pthread_cond_init(&cond, NULL); pthread_t tid[4]; void* (*fp[4])(void*) = {func1, func2, func3, func4}; for (int i = 0; i < 4; ++i) { pthread_create(&tid[i], NULL, fp[i], NULL); } for (int i = 0; i < 5; ++i) { printf("当前延时%d秒\n", i); sleep(1); } printf("father准备打钱了\n"); pthread_mutex_lock(&mutex); money += 1000; pthread_mutex_unlock(&mutex); pthread_cond_broadcast(&cond); for (int i = 0; i < 5; ++i) { printf("当前延时%d秒\n", i); sleep(1); } printf("father准备打钱了\n"); pthread_mutex_lock(&mutex); money += 1000; pthread_mutex_unlock(&mutex); pthread_cond_broadcast(&cond); for (int i = 0; i < 4; ++i) { pthread_join(tid[i],NULL); } pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; }

    终端显示:
    image

3|0条件变量生产者消费者模型

  1. 概念
    • 线程同步典型的案例即为生产者消费模型,而借助条件变量来实现这一模型,是比较常见的一种方法。假定有两个线程,一个线程模拟生产者行为,一个去模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加商品,消费就是消费者去消耗掉商品

__EOF__

本文作者若达萨罗
本文链接https://www.cnblogs.com/bcc0729/p/17672001.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   若达萨罗  阅读(26)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示