Linux——线程安全
Linux——线程安全
目录
一、线程安全
1.1 为什么需要线程安全
1.2 如何实现线程安全
二、举例
2.1 strtok函数
2.2 strtok_r函数
2.3 strtok_r函数+fork()
一、线程安全
线程安全(Thread Safety)是指在多线程环境中,程序的行为和结果是可预测的、一致的,并且不会由于多个线程同时访问和修改共享数据而导致数据损坏或产生不可预料的副作用。
多线程程序中,无论线程调度顺序如何,程序都能得到正确的结果,那么就处于线程安全的状态。
1.1 为什么需要线程安全
在多线程环境中,多个线程可能会同时访问和修改共享数据,如全局变量、静态变量、堆内存、文件等。如果不正确地管理这种并发访问,可能会导致以下问题:
-
数据竞争(Race Condition):多个线程同时访问和修改同一数据,导致数据不一致。
-
死锁(Deadlock):两个或多个线程相互等待对方释放资源,导致程序停止响应。
-
资源泄露(Resource Leak):由于线程间的同步不当管理,导致资源(如内存、文件句柄等)未被释放。
-
条件竞争条件(Condition Race Condition):程序的输出依赖于线程执行的顺序,导致结果不确定。
1.2 如何实现线程安全
1. 同步 2.使用线程安全的函数(可重入函数)
-
互斥锁(Mutex):
-
使用互斥锁来确保同一时间只有一个线程可以访问特定的代码段或数据。
-
例如,在 C/C++ 中可以使用
std::mutex
,在 POSIX 线程中可以使用pthread_mutex_t
。
-
-
读写锁(Read-Write Locks):
-
允许多个线程同时读取数据,但写入数据时需要独占。
-
适用于读多写少的场景。
-
-
条件变量(Condition Variables):
-
允许线程在等待特定条件满足时被唤醒。
-
通常与互斥锁一起使用。
-
-
信号量(Semaphore):
-
控制对共享资源的访问,确保同时访问的线程数量不超过一定值。
-
二、使用线程安全函数
2.1 strtok函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>// 线程函数定义
void* fun(void* arg) {char buff[] = "a b c d e f";char* s = strtok(buff, " "); // 初始化 strtokwhile (s != NULL) {printf("fun s=%s\n", s); // 打印分割后的字符串sleep(1); // 暂停1秒s = strtok(NULL, " "); // 继续分割字符串}
}int main() {pthread_t id; // 线程标识符// 创建线程pthread_create(&id, NULL, fun, NULL);// 定义字符串并使用 strtok 分割字符串char arr[] = "1 2 3 4 5 6";char* s = strtok(arr, " ");while (s != NULL) {printf("main s=%s\n", s);sleep(1); // 暂停1秒s = strtok(NULL, " "); // 继续分割字符串}// 等待线程结束pthread_join(id, NULL);exit(0); // 程序退出
}
![]()
线程分割abcd 主线程分割1 2 3 4
使用
strtok
函数来分割buff
。strtok
函数的第一个参数是要分割的字符串,第二个参数是分隔符。strtok
函数会继续从上次停止的地方开始分割字符串,直到没有更多的分隔符。线程启动后给strtok传的是abcd 所以之后不管是主函数或者其他函数打印都会是abcd
我们期望可以打印出1 2 3 4 5 6 和a b c d e f
得出结论 strtok函数不能在多线程中使用 内部有一个指针记录分割到哪里,但是strtok函数只有一个记录的 后传进来的会把前面传入的覆盖掉
2.2 strtok_r函数
线程安全的版本 加入各自线程用各自线程的记录指针,不让覆盖掉出现冲突
strtok_r
是线程安全的,它允许在多线程环境中安全地分割字符串,因为它不使用静态缓冲区来存储状态,通过一个额外的参数来保存上次分割的位置。相反,其他strtok
函数在处理多个字符串时可能会导致问题,因为它们使用静态缓冲区来存储状态。这意味着在多线程环境中,strtok
可能会导致竞争条件和未定义行为char *strtok_r(char *str, const char *delim, char **saveptr);
saveptr
:一个指向字符指针的指针,用于保存上次分割的位置
2.3 strtok_r函数+fork()
输出显示了进程ID(PID)和字符串分割的结果。
pid=22494
表示父进程的PID,pid=222496
表示子进程的PID。线程的PID与父进程相同,因为它是父进程创建的线程。