文章目录
-
线程生命周期
latex
线程创建 (pthread_create)
↓
线程执行 (start_routine)
↓
线程终止 → 方式1: pthread_exit()
方式2: 从入口函数return
方式3: 被取消 (pthread_cancel)
方式4: 进程终止
↓
线程回收 → 可连接线程(joinable): 必须pthread_join()
分离线程(detached): 自动回收
线程的终止
- 线程终止有4种方式:
- pthread_exit()
- 从入口函数return
- pthread_cancel
- 进程终止
pthread_exit()
- 若主线程调用 pthread_exit,主线程退出但子线程仍可继续运行
- 线程执行完入口函数后,等价于调用 pthread_exit(返回值)
函数原型
c
#include <pthread.h>
void pthread_exit(void *retval);
参数
- retval 是线程的退出状态(可被 pthread_join 捕获)
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
void *func (void *arg) {
printf("Hello thread!\n");
pthread_exit(NULL); //调用 pthread_exit(),指定一个退出状态值
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, func, NULL);
if(ret != 0){
errno = ret;
perror("pthread_create");
exit(EXIT_FAILURE);
}
printf("tid=%lu\n", tid);
pthread_join(tid, NULL);
return 0;
}
return
- 从
start_routine()的 return语句返回( 相当于调用pthread_exit())
c
void* thread_func(void* arg) {
// ... 线程工作 ...
return (void*)42; // 效果等同于pthread_exit
}
pthread_cancel()
- 线程被取消
- 取消是异步的:被取消线程可能不会立即终止
- 线程可以设置取消状态和取消类型来控制是否响应取消、以及在何时点响应
- 被取消的线程,其退出状态为 PTHREAD_CANCELED(通常是一个特殊值,如(void*)-1)
- 嵌入式系统慎用:可能导致资源未正确释放(如未解锁互斥锁)
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
void *func (void *arg) {
printf("Hello thread!\n");
sleep(10);
printf("thread done.\n"); //由于线程被主线程取消,所以这里不会被执行
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, func, NULL);
if(ret != 0){
errno = ret;
perror("pthread_create");
exit(EXIT_FAILURE);
}
sleep(1);
pthread_cancel(tid); // 1秒后取消
printf("tid=%lu\n", tid);
pthread_join(tid, NULL);
return 0;
}
进程终止
在线程中调用 exit() 会终止整个进程,这是非常危险的操作,通常应避免
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
void *func (void *arg) {
printf("Hello thread!\n");
exit(1); // 进程被终止,将导致所有线程终止
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, func, NULL);
if(ret != 0){
errno = ret;
perror("pthread_create");
exit(EXIT_FAILURE);
}
printf("tid=%lu\n", tid);
pthread_join(tid, NULL);
return 0;
}
线程的连接
pthread_join()
- 阻塞等待指定线程终止,并获取其退出状态(仅对「可结合线程」有效)
- 等待线程终止:阻塞调用线程,直到目标线程结束
- 获取线程返回值:接收线程的退出状态(void* 值)
- 释放线程资源:回收已终止线程的系统资源(如栈空间)
- 每个可连接线程必须被pthread_join()一次且仅一次,否则会造成资源泄漏
- 如果不对可连接线程调用pthread_join(),线程终止后会变成僵尸线程(类似僵尸进程),占用系统资源
调用后会阻塞,直到目标线程终止;若线程已终止,立即返回
函数原型
c
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数
- thread:要等待的线程 ID
- retval:输出参数,接收线程的退出状态(pthread_exit 的参数或入口函数返回值)
返回值
- 成功返回 0
- 失败返回错误码
ESRCH:找不到指定的线程ID(线程已结束或被误写)EINVAL:线程是分离的(detached),不可连接EDEADLK:死锁(例如,线程等待自己)
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *func (void *arg) {
printf("Hello thread!\n");
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, func, NULL);
if(ret != 0){
fprintf(stderr, "pthread_create:%s\n", strerror(ret) );
exit(EXIT_FAILURE);
}
printf("tid=%lu\n", tid);
ret = pthread_join(tid, NULL);
if(ret != 0){
fprintf(stderr, "pthread_join:%s\n", strerror(ret) );
exit(EXIT_FAILURE);
}
return 0;
}
线程的分离
- 线程的执行顺序是不确定的
- 线程是可连接的,或者是分离的
- 可连接的调用
pthread_join()等待线程终止并获取退出状态 - 分离的线程其资源会自动放回系统
- 可连接的调用
- 分离线程无法被
<font style="color:rgb(0, 0, 0);">pthread_join</font>等待,调用会报错
两种线程对比
| 特性 | 可连接线程 (Joinable) | 分离线程 (Detached) |
|---|---|---|
| 资源回收 | 必须显式调用pthread_join() |
自动回收,线程终止后立即释放资源 |
| 获取返回值 | 可通过pthread_join()获取 |
无法获取返回值 |
| 默认状态 | pthread_create()创建时的默认状态 |
需要显式设置 |
| 适用场景 | 需要知道线程执行结果 | 一次性后台任务,不关心结果 (如日志线程) |
设置线程分离方式
- 设置线程分离状态的方式:
- 创建后分离(动态分离)
- 创建时分离(推荐)
创建后分离(动态分离)
pthread_detach
- 将线程设为「分离状态」,线程终止后自动释放资源(无需 pthread_join)
函数原型
c
#include <pthread.h>
int pthread_detach(pthread_t thread);
- 返回值:成功返回 0,失败返回错误码
主线程中分离
c
pthread_t tid;
pthread_create(&tid, NULL, thread_func, NULL);
pthread_detach(tid); // 主线程中分离
在线程内部分离自己
pthread_self()获取当前执行线程的唯一标识符(线程 ID)
c
// 在线程内部分离自己
void* thread_func(void* arg) {
pthread_detach(pthread_self());
// ... 线程工作 ...
return NULL;
}
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
void *func (void *arg) {
pthread_detach(pthread_self()); //设置线程为分离属性
printf("detach thread.\n");
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, func, NULL);
if(ret != 0){
errno = ret;
perror("pthread_create");
exit(EXIT_FAILURE);
}
// pthread_detach(tid); //也可以在主线程当中设置创建线程的分离属性
printf("tid=%lu\n", tid);
sleep(1);
return 0;
}
如果线程在pthread_detach()调用前就终止了,可能会变成僵尸线程
创建时分离(推荐)
- 最安全的方式,确保线程一开始就是分离的
c
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_t tid;
pthread_create(&tid, &attr, thread_func, NULL);
pthread_attr_destroy(&attr); // 销毁属性对象
pthread_attr_setdetachstate
函数原型
c
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数
- attr:指向已初始化的线程属性对象(pthread_attr_t)的指针
- detachstate:要设置的分离状态,仅支持两个取值:
PTHREAD_CREATE_JOINABLE(默认):可结合状态PTHREAD_CREATE_DETACHED:分离状态
返回值
- 成功:返回0
- 失败:返回非 0 的错误码(常见错误:EINVAL,如attr 无效或 detachstate取值非法)
c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *thread_func(void *arg) {
printf("分离线程执行中(tid=%lu)\n", (unsigned long)pthread_self());
// 分离线程终止后,资源自动回收
pthread_exit(NULL);
}
int main() {
pthread_attr_t attr;
// 1. 初始化属性对象
if (pthread_attr_init(&attr) != 0) {
perror("pthread_attr_init failed");
exit(1);
}
// 2. 设置分离状态为 DETACHED
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
fprintf(stderr, "pthread_attr_setdetachstate failed: %s\n", strerror(ret));
pthread_attr_destroy(&attr);
exit(1);
}
// 3. 创建分离线程
pthread_t tid;
if (pthread_create(&tid, &attr, thread_func, NULL) != 0) {
perror("pthread_create failed");
pthread_attr_destroy(&attr);
exit(1);
}
printf("创建分离线程(tid=%lu)\n", (unsigned long)tid);
// 4. 销毁属性对象
pthread_attr_destroy(&attr);
// 主线程休眠,确保分离线程执行完成
sleep(1);
return 0;
}
嵌入式开发中的特殊考虑:
资源有限性:在内存受限的嵌入式系统中,分离线程可以减少资源泄漏的风险,因为不需要记住去join每个线程
实时性要求:分离线程可以立即释放资源,避免因等待join而延迟其他任务的执行
除非需要获取线程的返回值,否则优先使用分离线程