Linux 之 【多线程】(STL、智能指针与线程安全、读者写者问题)

目录

1.STL、智能指针与线程安全

STL容器默认不是线程安全的

智能指针线程安全

2.读者写者问题

背景

读写锁核心特性

321原则

优先级策略

POSIX读写锁接口

读写锁示例

设置写者优先示例


1.STL、智能指针与线程安全

STL容器默认不是线程安全的

多线程环境下需要调用者自行保证线程安全

常见方案:使用外部锁(如std::mutex)保护容器操作

智能指针线程安全

1.unique_ptr(独享所有权)

  • 线程安全特性:不涉及线程安全问题

  • 原因:只在当前代码块范围内生效,独占资源所有权,不存在多个线程共享的情况

  1. shared_ptr(共享所有权)

两部分需要区分对待

组成部分 线程安全性 说明
引用计数 线程安全 标准库基于原子操作(CAS)实现,保证高效原子操作
管理的资源 不保证安全 智能指针只负责管理资源生命周期,不保护资源内容的并发访问

关键理解

  • 引用计数的增减是原子操作,多个线程同时操作shared_ptr不会导致计数错乱

  • 但通过shared_ptr访问的资源对象本身,需要调用者自行加锁保护

2.读者写者问题

背景

多读少写场景:某些公共数据修改机会少,但读取查找操作频繁且耗时。传统互斥锁会严重降低并发效率,需要专门机制处理

读写锁核心特性

特性 说明
写独占 写者加锁时,其他读者和写者都不能访问
读共享 多个读者可以同时加锁读取数据
优先级 可设置读者优先或写者优先

321原则

维度 内容 说明
3种关系 写写互斥 多个写者不能同时写
写读互斥、同步 写和读不能同时进行,且需数据同步
读读共享 多个读者可以同时读
2种角色 读者、写者 两类线程操作数据
1个交易场所 共享数据 数据交换的内存区域

优先级策略

策略 特点 问题 适用场景
读者优先(默认) 读者持续进入时,写者被阻塞 写者可能饥饿 读极多、写可延迟
写者优先 写者到达后阻塞后续读者 读者可能饥饿 写操作需及时响应

读者优先策略下,只要存在任何读者正在读取数据,后续到达的读者可以立即获得读锁继续读取,而写者必须等待所有读者释放锁后才能写入。这种策略适合读操作极其频繁、写操作可以延迟的场景,但可能导致写者饥饿------当读者持续不断地到来时,写者可能永远无法获得写锁
写者优先策略下,一旦有写者到达并等待写锁,后续新到达的读者将被阻塞,不能插队读取,直到等待的写者完成写入操作。这种策略确保写操作能及时执行,适合需要数据及时更新的场景,但可能造成读者饥饿------如果写者频繁出现,读者可能长时间无法获取读锁。

POSIX读写锁接口

复制代码
// 设置优先级
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 - 写者优先,写者不可递归
*/

// 初始化和销毁
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_wrlock(pthread_rwlock_t *rwlock);  // 写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);  // 解锁

读写锁示例

复制代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

// 共享数据
int shared_data = 0;
// 读写锁
pthread_rwlock_t rwlock;

// 读者线程
void* reader(void* arg) {
    int id = *(int*)arg;
    while (1) {
        // 加读锁
        pthread_rwlock_rdlock(&rwlock);
        printf("读者 %d 读取数据: %d\n", id, shared_data);
        // 模拟读取耗时
        usleep(100000);
        pthread_rwlock_unlock(&rwlock);
        
        // 间隔一段时间再读
        usleep(500000);
    }
    return NULL;
}

// 写者线程
void* writer(void* arg) {
    int id = *(int*)arg;
    while (1) {
        // 加写锁
        pthread_rwlock_wrlock(&rwlock);
        shared_data++;
        printf("写者 %d 修改数据为: %d\n", id, shared_data);
        // 模拟写入耗时
        usleep(200000);
        pthread_rwlock_unlock(&rwlock);
        
        // 间隔一段时间再写
        usleep(2000000);
    }
    return NULL;
}

int main() {
    // 初始化读写锁(默认读者优先)
    pthread_rwlock_init(&rwlock, NULL);
    
    pthread_t readers[3], writers[2];
    int reader_ids[3] = {1, 2, 3};
    int writer_ids[2] = {1, 2};
    
    // 创建3个读者线程
    for (int i = 0; i < 3; i++) {
        pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
    }
    
    // 创建2个写者线程
    for (int i = 0; i < 2; i++) {
        pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
    }
    
    // 运行一段时间后退出(示例用sleep)
    sleep(10);
    
    // 销毁读写锁
    pthread_rwlock_destroy(&rwlock);
    return 0;
}

设置写者优先示例

复制代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_rwlock_t rwlock;
int shared_counter = 0;

void* reader_func(void* arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 5; i++) {
        pthread_rwlock_rdlock(&rwlock);
        printf("读者 %d 读取: %d\n", id, shared_counter);
        usleep(50000);  // 模拟读取
        pthread_rwlock_unlock(&rwlock);
        usleep(100000);
    }
    return NULL;
}

void* writer_func(void* arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 3; i++) {
        pthread_rwlock_wrlock(&rwlock);
        shared_counter++;
        printf("写者 %d 写入: %d\n", id, shared_counter);
        usleep(100000);  // 模拟写入
        pthread_rwlock_unlock(&rwlock);
        usleep(500000);
    }
    return NULL;
}

int main() {
    // 设置写者优先
    pthread_rwlockattr_t attr;
    pthread_rwlockattr_init(&attr);
    pthread_rwlockattr_setkind_np(&attr, 
        PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
    
    pthread_rwlock_init(&rwlock, &attr);
    
    pthread_t readers[5], writers[2];
    int reader_ids[5] = {1, 2, 3, 4, 5};
    int writer_ids[2] = {1, 2};
    
    // 创建线程
    for (int i = 0; i < 5; i++) {
        pthread_create(&readers[i], NULL, reader_func, &reader_ids[i]);
    }
    for (int i = 0; i < 2; i++) {
        pthread_create(&writers[i], NULL, writer_func, &writer_ids[i]);
    }
    
    // 等待线程结束
    for (int i = 0; i < 5; i++) {
        pthread_join(readers[i], NULL);
    }
    for (int i = 0; i < 2; i++) {
        pthread_join(writers[i], NULL);
    }
    
    pthread_rwlock_destroy(&rwlock);
    pthread_rwlockattr_destroy(&attr);
    return 0;
}
相关推荐
mqffc2 小时前
Linux(CentOS)安装 MySQL
linux·mysql·centos
IT 行者2 小时前
OpenClaw 浏览器自动化测试的那些坑(一):Linux Snap 版本的 Chromium 无法使用托管模式
linux·运维·服务器·人工智能
Tangcan-3 小时前
在Ubuntu 22.04上安装redis
linux·redis·ubuntu
麦德泽特3 小时前
STM32与Linux的无缝协作:通过USB CDC/VCP实现高效数据交互
linux·stm32·单片机·嵌入式硬件·安全
RisunJan4 小时前
Linux命令-lvdisplay(显示逻辑卷属性)
linux·运维·服务器
番茄灭世神4 小时前
Linux从入门到进阶 第四章
linux·操作系统·计算机基础
rfidunion5 小时前
springboot+VUE+部署(11。Nginx)
linux·vue.js·nginx
Trouvaille ~5 小时前
【Linux】网络层与IP协议详解:从地址到路由的完整指南
linux·运维·服务器·网络·tcp/ip·路由器·网络层
袁袁袁袁满5 小时前
Ubuntu怎么查看系统日志?
linux·运维·服务器·ubuntu