linux学习笔记(25)——线程安全
线程安全
线程安全:要保证线程安全需要做到:
对线程同步,保证同一时刻只有一个线程访问临界资源。
在多线程中使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个
函数能被多个线程同时调用且不发生竞态条件,则我们说它是线程安全的
识别线程安全问题
危险信号:
多个线程访问共享数据
至少一个线程修改数据
没有适当的同步机制
安全模式:
✅ 只读访问(所有线程只读)
✅ 线程局部数据(每个线程有自己的副本)
✅ 不可变对象(创建后不修改)
✅ 正确同步的访问(用锁、信号量等保护)
线程安全的核心:
- 识别共享数据:找到所有被多个线程访问的数据
- 保护临界区:用锁、信号量等同步机制保护
- 避免竞态条件:确保操作的原子性
- 设计时考虑:在架构阶段就考虑线程安全
没有同步保护的共享可变数据 = 线程安全灾难!
线程安全问题:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
void* fun(void* arg){char buff[] = "a b c d e f";char* s = strtok(buff, " ");while (s != NULL){printf("fun s=%s\n", s);sleep(1);s = strtok(NULL, " ");}return NULL;
}
int main(){pthread_t id;pthread_create(&id, NULL, fun, NULL);char buff[] = "1 2 3 4 5 6";char* s = strtok(buff, " ");while (s != NULL){printf("main s=%s\n", s);sleep(1);s = strtok(NULL, " ");}return 0;
}
strtok 函数在内部使用静态缓冲区来保存字符串分割的进度。当主线程和 fun 线程同时调用 strtok 时,它们会共享这个单一的内部状态,导致字符串处理相互干扰,最终可能打印出错误的结果,甚至引起程序崩溃。
解决办法:函数替换:将所有 strtok 替换为 strtok_r。
题外话:
多线程种某个线程调用 fork(),子进程只有一条执行路径,就是fork所在的执行路径
当进程中的某个线程调用 fork() 时,子进程只会复制调用 fork() 的那个线程,其他线程不会被复制到子进程中。也就是说,子进程在创建后,只有一个执行流(即调用 fork() 的那个线程对应的执行流)。
父进程被加锁的互斥锁 fork 后在子进程中是已经加锁的状态。
并且子进程中只有调用 fork 的那个线程,若后续子进程中的代码要操作该互斥锁,可能会因为锁已经被 “复制过来的加锁状态” 而出现问题,比如死锁(如果子进程尝试再次加锁该互斥锁,而互斥锁是不可重入的)。