目录
1.STL、智能指针与线程安全
STL容器默认不是线程安全的
多线程环境下需要调用者自行保证线程安全
常见方案:使用外部锁(如std::mutex)保护容器操作
智能指针线程安全
1.unique_ptr(独享所有权)
-
线程安全特性:不涉及线程安全问题
-
原因:只在当前代码块范围内生效,独占资源所有权,不存在多个线程共享的情况
- 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;
}