Linux 线程常见函数超详细讲解
·
一、POSIX 线程库概述
Linux 下的线程函数都属于 POSIX 线程库(pthread),绝大多数函数名以pthread_开头。使用时需注意:
- 头文件:
#include <pthread.h> - 编译链接:需加
-lpthread选项(如gcc main.c -o main -lpthread) - 错误处理:pthread 函数出错时不设置全局 errno,而是通过返回值直接返回错误码(读取返回值比读取线程内 errno 开销更小)。
二、核心线程控制函数详解
1. pthread_create —— 创建线程
功能
在进程中创建一个新的执行流(线程),新线程会立即执行指定的回调函数。
函数原型
int pthread_create(
pthread_t *thread, // 输出参数:返回新线程的ID
const pthread_attr_t *attr, // 线程属性,NULL表示使用默认属性
void *(*start_routine)(void*), // 线程启动后执行的函数指针
void *arg // 传给start_routine的参数
);
参数详解
-
thread:指向pthread_t类型变量的指针。- 结合课件底层原理:
pthread_t本质是进程地址空间中线程控制块(TCB)的地址(对应 glibc 的struct pthread*)。 - 新线程创建成功后,其 TCB 地址会被写入该指针指向的变量。
- 结合课件底层原理:
-
attr:线程属性对象指针,通常传NULL使用默认属性。- 可设置栈大小、分离状态、调度优先级等(后文会单独讲属性函数)。
-
start_routine:线程入口函数指针,类型为void* (*)(void*)。- 线程启动后会自动调用该函数,函数返回 / 退出时线程终止。
-
arg:传给start_routine的参数。- 若需传多个参数,可封装为结构体后传指针。
返回值
- 成功:返回
0; - 失败:返回错误码(如
EAGAIN表示系统资源不足,EINVAL表示属性无效)。
底层原理
根据课件中 glibc 源码分析,pthread_create内部主要做三件事:
- 分配 TCB 与栈:调用
ALLOCATE_STACK宏,通过mmap在进程共享区分配线程栈和 TCB(struct pthread); - 填充 TCB:设置入口函数
start_routine、参数arg、线程 ID 等; - 调用 clone 系统调用:内核创建轻量级进程(LWP),并开始执行线程入口逻辑。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// 线程入口函数
void* thread_func(void* arg) {
char* msg = (char*)arg;
for (int i = 0; i < 3; i++) {
printf("%s: 第%d次执行\n", msg, i+1);
sleep(1);
}
return NULL;
}
int main() {
pthread_t tid;
// 创建线程,传入参数"新线程"
int ret = pthread_create(&tid, NULL, thread_func, "新线程");
if (ret != 0) {
fprintf(stderr, "创建线程失败: %d\n", ret);
return 1;
}
// 主线程也执行
thread_func("主线程");
return 0;
}
2. pthread_self —— 获取当前线程 ID
功能
获取当前调用线程的pthread_t类型 ID。
函数原型
pthread_t pthread_self(void);
关键说明
- 返回的
pthread_t是用户态线程库维护的 ID(TCB 地址),而非内核的 LWP ID; - 内核 LWP ID 可通过
syscall(SYS_gettid)获取(需包含<sys/syscall.h>); - 结合课件:用
ps -aL可查看线程的 LWP(LWP列),其中与PID相同的是主线程。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <unistd.h>
void* thread_func(void* arg) {
pthread_t tid = pthread_self();
pid_t lwp = syscall(SYS_gettid); // 获取内核LWP ID
printf("用户态线程ID: %p, 内核LWP ID: %d\n", (void*)tid, lwp);
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
pthread_join(tid, NULL);
return 0;
}
3. pthread_exit —— 终止当前线程(3、6)
功能
主动终止当前调用线程,而不影响进程内其他线程。
函数原型
void pthread_exit(void *value_ptr);
参数与注意事项
value_ptr:线程的退出值,会被pthread_join获取。- ⚠️ 绝对不能指向线程栈上的局部变量:线程退出后栈空间会被回收,该地址变为无效;
- 应指向全局变量、动态分配内存(malloc) 或静态变量。
与return的区别
- 在线程入口函数中
return也会终止线程,返回值同样会被pthread_join获取; - 但
pthread_exit可以在入口函数的子函数中调用,直接终止线程(return只能从当前子函数返回)。
底层原理(结合课件)
线程退出时,会将value_ptr写入自己 TCB 的result字段,等待pthread_join来读取。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
void* thread_func(void* arg) {
int* ret = malloc(sizeof(int));
*ret = 100; // 退出值设为100
pthread_exit(ret); // 终止线程,返回ret
// 下面的代码不会执行
printf("这行不会打印\n");
return NULL;
}
int main() {
pthread_t tid;
void* result;
pthread_create(&tid, NULL, thread_func, NULL);
pthread_join(tid, &result);
printf("线程退出值: %d\n", *(int*)result);
free(result); // 记得释放动态内存
return 0;
}
4. pthread_join —— 等待指定线程终止
pthread_joni使用理解与误区讲解
功能
阻塞当前线程,直到指定的线程终止,并获取其退出值。
函数原型
int pthread_join(pthread_t thread, void **value_ptr);
参数详解
thread:要等待的线程 ID(pthread_t类型)。value_ptr:二级指针,输出参数。- 若不为
NULL,线程的退出值会被写入该指针指向的void*变量; - 结合之前的误区:这里是 “通过指针修改另一个指针的值”,因此必须用二级指针。
- 若不为
返回值
- 成功:返回
0; - 失败:返回错误码(如
ESRCH表示线程不存在,EINVAL表示线程不可 join)。
线程终止状态与退出值的对应(结合课件)
根据课件内容,线程终止方式不同,value_ptr获取到的值也不同:
表格
| 线程终止方式 | *value_ptr的值 |
|---|---|
入口函数return返回 |
线程函数的返回值 |
调用pthread_exit终止 |
pthread_exit的参数value_ptr |
被pthread_cancel取消 |
常量PTHREAD_CANCELED((void*)-1) |
关键注意事项
- 线程默认是joinable状态,必须被
pthread_join,否则线程退出后资源(TCB、栈)无法释放,造成内存泄漏; - 一个线程只能被
pthread_join一次,多次 join 会导致未定义行为。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
// 方式1:return返回
void* thread1(void* arg) {
int* ret = malloc(sizeof(int));
*ret = 1;
return ret;
}
// 方式2:pthread_exit终止
void* thread2(void* arg) {
int* ret = malloc(sizeof(int));
*ret = 2;
pthread_exit(ret);
}
// 方式3:被cancel取消
void* thread3(void* arg) {
while (1) {
printf("线程3运行中...\n");
sleep(1); // sleep是取消点
}
return NULL;
}
int main() {
pthread_t tid;
void* result;
// 测试return
pthread_create(&tid, NULL, thread1, NULL);
pthread_join(tid, &result);
printf("线程1退出值: %d\n", *(int*)result);
free(result);
// 测试pthread_exit
pthread_create(&tid, NULL, thread2, NULL);
pthread_join(tid, &result);
printf("线程2退出值: %d\n", *(int*)result);
free(result);
// 测试cancel
pthread_create(&tid, NULL, thread3, NULL);
sleep(3);
pthread_cancel(tid); // 取消线程3
pthread_join(tid, &result);
if (result == PTHREAD_CANCELED) {
printf("线程3被取消\n");
}
return 0;
}
5. pthread_detach —— 分离线程
功能
将指定线程设置为detached(分离)状态,线程退出时系统会自动释放其资源,无需pthread_join。
函数原型
int pthread_detach(pthread_t thread);
参数与返回值
thread:要分离的线程 ID;- 返回值:成功返回
0,失败返回错误码。
关键说明
- 可以由其他线程调用
pthread_detach(tid)分离目标线程,也可以由线程自身调用pthread_detach(pthread_self()); - joinable 与 detached 冲突:一个线程不能同时是两种状态,分离后不能再
pthread_join; - 若不关心线程返回值,推荐使用分离状态,避免资源泄漏。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* thread_func(void* arg) {
pthread_detach(pthread_self()); // 自身分离
printf("分离线程运行,退出后自动释放资源\n");
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
sleep(1); // 等待线程执行完
// 无需pthread_join
return 0;
}
6. pthread_cancel —— 终止指定线程
功能
向指定线程发送取消请求,请求其终止执行。
函数原型
int pthread_cancel(pthread_t thread);
关键说明
pthread_cancel只是发送请求,不会等待线程终止;- 线程是否响应取消、何时响应,取决于取消状态和取消类型:
- 取消状态:默认是
PTHREAD_CANCEL_ENABLE(允许取消),可设为PTHREAD_CANCEL_DISABLE(禁止取消); - 取消类型:默认是
PTHREAD_CANCEL_DEFERRED(延迟取消,到达取消点才终止),可设为PTHREAD_CANCEL_ASYNCHRONOUS(异步取消,立即终止)。
- 取消状态:默认是
- 取消点:线程会检查取消请求的函数,如
sleep、read、write、pthread_testcancel等。
使用示例
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* thread_func(void* arg) {
// 设置为允许取消(默认),延迟取消(默认)
while (1) {
printf("线程运行中...\n");
sleep(1); // sleep是取消点,会在此处响应取消
}
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
sleep(3);
pthread_cancel(tid); // 发送取消请求
pthread_join(tid, NULL); // 等待线程终止
printf("线程已取消\n");
return 0;
}
三、线程属性函数(扩展)
课件中提到了struct pthread_attr结构体,用于设置线程属性。常用属性函数如下:
1. 初始化 / 销毁属性对象
int pthread_attr_init(pthread_attr_t *attr); // 初始化属性为默认值
int pthread_attr_destroy(pthread_attr_t *attr); // 销毁属性对象
2. 设置 / 获取分离状态
// 设置分离状态:PTHREAD_CREATE_JOINABLE(默认)或PTHREAD_CREATE_DETACHED
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
3. 设置 / 获取栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
- 默认栈大小通常为 8MB,可根据需求调整(如嵌入式环境可减小栈大小)。
属性使用示例
#include <stdio.h>
#include <pthread.h>
void* thread_func(void* arg) {
printf("分离线程运行\n");
return NULL;
}
int main() {
pthread_t tid;
pthread_attr_t attr;
// 初始化属性
pthread_attr_init(&attr);
// 设置为分离状态
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 用属性创建线程
pthread_create(&tid, &attr, thread_func, NULL);
// 销毁属性
pthread_attr_destroy(&attr);
// 无需join,线程退出自动释放资源
pthread_exit(NULL); // 主线程退出,不影响新线程
return 0;
}
四、调试辅助函数(非标准)
介绍两个用于调试的非标准函数(_np表示 “non-portable”,不可移植):
// 设置当前线程名称(Linux下最大16字符,包括\0)
int pthread_setname_np(pthread_t thread, const char *name);
// 获取当前线程名称
int pthread_getname_np(pthread_t thread, char *name, size_t len);
- 用
ps -aL可查看线程名称,方便多线程调试。
五、常见函数使用总结与避坑指南
- 错误处理:始终检查 pthread 函数的返回值,不要依赖 errno;
- 返回值内存管理:线程返回值若指向动态内存,主线程
join后需free; - 避免局部变量返回:
pthread_exit或return的参数不能是线程栈局部变量; - 资源释放:joinable 线程必须
join,或设为 detached 状态,否则内存泄漏; - 取消安全:使用
pthread_cancel时注意取消点,避免资源未释放就终止。
更多推荐




所有评论(0)