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;
}
相关推荐
kymjs张涛2 小时前
OpenClaw 学习小组:初识
android·linux·人工智能
程序设计实验室4 小时前
经历分享,发现挖矿木马后,服务器快速备份与重装(腾讯云平台)
linux
Miku165 小时前
OpenClaw-Linux+飞书官方Plugin安装指南
linux·人工智能·agent
Miku165 小时前
OpenClaw 接入 QQ Bot 完整实践指南
linux·人工智能·agent
Yogurt_cry10 小时前
[树莓派4B] 闲置近10年的爱普生 L310 打印机爆改无线打印机
linux·物联网·树莓派
Johny_Zhao1 天前
OpenClaw中级到高级教程
linux·人工智能·信息安全·kubernetes·云计算·yum源·系统运维·openclaw
Sheffield2 天前
Docker的跨主机服务与其对应的优缺点
linux·网络协议·docker
Sheffield2 天前
Alpine是什么,为什么是Docker首选?
linux·docker·容器
Johny_Zhao3 天前
centos7安装部署openclaw
linux·人工智能·信息安全·云计算·yum源·系统运维·openclaw