线程的终止、连接与分离

文章目录

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而延迟其他任务的执行

除非需要获取线程的返回值,否则优先使用分离线程

相关推荐
獭.獭.2 个月前
Linux -- 线程控制
linux·pthread·线程分离·线程取消·线程局部存储·lwp·线程栈
椿融雪2 年前
【Linux】Linux线程概念和线程控制
linux·服务器·线程·线程控制·线程概念·pthread_create·线程分离