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;
}
相关推荐
人生百态,人生如梦3 小时前
Ubuntu在低版本下使用ccswitch+claudecode(CCSwitch+Docker+Claude)连调教程
linux·ubuntu·docker·claudecode·ccswitch
byte轻骑兵3 小时前
【BlueZ】hciconfig 工具:蓝牙适配器管理实操 + 源码实现基础
linux·bluez·电脑蓝牙·嵌入式蓝牙
SKH.3 小时前
Linux驱动-系统移植
linux·服务器
根目录下的猫3 小时前
虚拟机复制过来运行时:“虚拟机使用的此版本,VMware Workstation 不支持的硬件版本。”错误解决办法,亲测可用
linux·运维·服务器·后端
yunwei373 小时前
eBPF 入门实践教程十七:编写 eBPF 程序统计随机/顺序磁盘 I/O
linux·后端·性能优化
帷幕落秋4 小时前
内存、虚拟内存与 OOM
linux·运维
dingdingfish4 小时前
TLPI 第41章 读书笔记:Fundamentals of Shared Libraries
linux·static·link·dynamic·library·tlpi
Apipi*4 小时前
30天速通Linux 第五章Linux 进程管理
linux·运维·服务器
杨云龙UP4 小时前
PostgreSQL 四种常见部署模式快速理解:单实例、流复制、repmgr、Patroni
linux·运维·数据库·postgresql·流复制·高可用ha
流浪0014 小时前
Linux系统篇46——线程(十一) 生产者消费者模型,三种关系和一个交易场所
linux·线程·并发·同步·互斥·阻塞队列·生产者消费者模型