Linux 线程学习总结:创建、等待、退出、分离
Linux 线程学习总结:创建、等待、退出、分离(含实战代码与避坑)
前言
这段时间通过手写四段核心代码,系统学习了 Linux 下 POSIX 线程的基本使用,包括线程创建、线程等待、线程退出、线程分离四大核心知识点。本文基于自己写的代码、遇到的问题、理解误区,做一次完整总结,方便后续复习回顾与面试使用。
一、线程基本概念
- 线程是进程内部的执行流,共享进程地址空间。 一个进程可以创建多个线程,轻量级、切换成本低。
- 线程共享:代码段、数据段、文件描述符、全局变量等。
- 线程独有:栈、上下文寄存器、信号屏蔽字、线程局部存储。
- 新创建的线程默认是joinable 状态,必须被等待,否则资源泄漏。
二、线程创建 —— pthread_create
这是最基础的 API,用于创建一个新线程。
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg
);
参数说明
• thread:输出型参数,返回线程 ID
• attr:线程属性,NULL 表示使用默认属性
• start_routine:线程入口函数
• arg:传递给线程的参数
返回值
成功返回 0,失败返回错误码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *rout(void *arg)
{
for (;;)
{
printf("I'am thread 1\n");
sleep(1);
}
}
int main(void)
{
pthread_t tid;
int ret;
if ((ret = pthread_create(&tid, NULL, rout, NULL)) != 0)
{
fprintf(stderr, "pthread_create: %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
for (;;)
{
printf("I'am main thread\n");
sleep(1);
}
}
注意事项
编译必须加 -pthread,否则会出现链接错误。
线程创建后与主线程并发运行。
主线程如果提前退出,整个进程会结束,所有线程一起终止。
三、线程等待 —— pthread_join
为什么需要线程等待?
- 默认线程是 joinable,(新创建出来的线程,默认就是 joinable 状态。
意思就是:这个线程必须被人 “回收”,否则会产生内存泄漏!)退出后资源不会自动释放,会造成泄漏。 - 已退出线程的空间仍留在进程地址空间。
- 新线程不会复用旧线程资源。
- 可以获取线程退出返回值。
函数原型
int pthread_join(pthread_t thread, void **value_ptr);
功能
阻塞等待指定线程结束
回收线程内核资源
获取线程退出码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
/*
为什么需要线程等待?
• 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
• 创建新的线程不会复⽤刚才退出线程的地址空间。
功能:等待线程结束
原型
int pthread_join(pthread_t thread, void **value_ptr);
参数:
thread:线程ID
value_ptr:它指向⼀个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码
*/
void *thread1(void *arg)
{
printf("thread 1 returning ... \n");
// 这里我创建一个内存资源,在堆上创建,需要手动释放
int *p = (int *)malloc(sizeof(int));
*p = 1;
// return:函数返回,线程也结束
return (void *)p;
}
void *thread2(void *arg)
{
printf("thread 2 exiting ...\n");
// 同上
int *p = (int *)malloc(sizeof(int));
*p = 2;
/*
只能自己调用自己
退出后线程资源不会立刻释放,要等别人 join
主线程用它,不会结束整个进程,只会结束主线程自己
*/
pthread_exit((void *)p);
}
void *thread3(void *arg)
{
while (1)
{ //
printf("thread 3 is running ...\n");
sleep(1);
}
return NULL;
}
int main(void)
{
pthread_t tid;
void *ret;
// thread 1 return
pthread_create(&tid, NULL, thread1, NULL);
// 需要手动释放线程资源,然后这里等待线程退出
pthread_join(tid, &ret);
printf("thread return, thread id %lx, return code:%d\n", tid, *(int *)ret);
// 需要手动释放
free(ret);
// thread 2 exit
// 也是一样
pthread_create(&tid, NULL, thread2, NULL);
// 等待退出
pthread_join(tid, &ret);
printf("thread return, thread id %lx, return code:%d\n", tid, *(int *)ret);
free(ret);
// thread 3 cancel by other
pthread_create(&tid, NULL, thread3, NULL);
sleep(3);
// 取消线程,需要join
pthread_cancel(tid);
pthread_join(tid, &ret);
if (ret == PTHREAD_CANCELED)
printf("thread return, thread id %lx, return code:PTHREAD_CANCELED\n", tid);
else
printf("thread return, thread id %lx, return code:NULL\n", tid);
}
关键点
join 会阻塞,直到线程退出。
线程返回的堆空间需要手动 free。
不 join 一定会导致线程资源泄漏。
四、线程退出的三种方式
线程退出有三种典型方式,行为略有差异,面试高频考点。
- return 退出
在线程函数中 return,线程正常结束。效果等价于 pthread_exit。
return (void *)p;
- pthread_exit 主动退出
线程自己终止自己,不会结束整个进程
pthread_exit((void *)p);
特点:
只能自己调用自己
立即终止线程
主线程调用不会杀死整个进程
3. pthread_cancel 被其他线程取消
一个线程取消另一个线程。
pthread_cancel(tid2);
特点:
不是立即杀死线程
必须到达取消点才真正退出
取消点如:sleep、printf、cout、read、write 等
被取消线程的退出码是 PTHREAD_CANCELED
五、线程分离 —— pthread_detach
为什么要分离?
joinable 线程必须 join,否则泄漏。
若不关心返回值,join 是多余负担。
分离线程退出时系统自动释放资源,无需 join。
使用方式
pthread_detach(pthread_self());
特点:
分离后不能再被 pthread_join
join 会直接失败
线程退出自动释放资源
适合后台服务线程、工作线程
#include <iostream>
#include <unistd.h>
#include <string.h>
/*
分离线程
• 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进⾏pthread_join操作,否则
⽆法释放资源,从⽽造成系统泄漏。
• 如果不关⼼线程的返回值,join是⼀种负担,这个时候,我们可以告诉系统,当线程退出时,⾃
动释放线程资源。
*/
#include <pthread.h>
void *thread_run(void *arg)
{
// 我自己分离出去,不要主线程来释放我了,我没有返回值,所不会造成资源泄漏
pthread_detach(pthread_self());
printf("%s\n", (char *)arg);
return NULL;
}
int main(void)
{
pthread_t tid;
if (pthread_create(&tid, NULL, thread_run, (void *)"thread1 run...") != 0)
{
printf("create thread error\n");
return 1;
}
int ret = 0;
sleep(1);
// 让主线程等子线程分离,否则主线程跑太快了,子线程还没分离呢,就让主线程等到了,就直接给释放了
if (pthread_join(tid, NULL) == 0)
{
printf("pthread wait success\n");
ret = 0;
}
// 然后子线程分离了,主线程就等待失败了
else
{
printf("pthread wait failed\n");
ret = 1;
}
return ret;
}
运行结果必然是 join 失败。
六、joinable 与 detached 对比总结
joinable(默认)
• 需要 pthread_join
• 可获取返回值
• 不 join → 资源泄漏
detached(分离状态)
• 无需 join
• 自动释放资源
• 不能被 join
• 适合后台长期运行线程
七、线程 API 速记
pthread_create 创建线程 成功返回 0
pthread_join 等待并回收线程 阻塞、获取返回值
pthread_exit 线程主动退出 不终止进程
pthread_cancel 取消另一个线程 依赖取消点
pthread_detach 设置线程分离 自动释放资源
pthread_self 获取自身线程 ID 常用
八、总结
线程默认 joinable,必须 join,否则泄漏。
线程退出三种方式:return、pthread_exit、pthread_cancel。
pthread_cancel 不是立即杀死线程,必须到达取消点。
线程不要返回局部变量地址。
分离线程自动释放资源,无需 join。
主线程不能提前退出,否则所有线程一起结束。
编译必须加 -pthread。
更多推荐




所有评论(0)