Linux线程 | 创建 终止 回收 分离

本文介绍了Linux线程的基本概念和操作,包括线程的创建(pthread_create)、线程函数、线程的自然终止(pthread_exit)、线程的强制终止(pthread_cancel)、线程的回收(pthread_join)以及线程的分离(pthread_detach)。线程的回收和分离对于管理资源和防止僵尸线程至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线程简介

图片1

  • 线程是参与系统调度的最小单位。它被包含在进程之中,是进程中的实际运行单位。

  • 一个进程中可以创建多个线程,多个线程实现并发运行,每个线程执行不同的任务。

  • 每个线程都有其对应的标识,称为线程 ID,线程 ID 使用 pthread_t 数据类型来表示。

二、线程的创建

线程是轻量级的并发执行单元,通过调用Linux系统提供的pthread库中的函数来创建和管理线程。

  • 包含头文件:
#include <pthread.h>
  • 定义线程函数:

线程函数是线程实际执行的函数,可以是任何可以被调用的函数。线程函数的原型如下:

void* thread_function(void* arg);

其中arg是传递给线程函数的参数,可以是任何类型的数据。线程函数的返回值为void*类型,可以返回任何类型的数据。

  • 创建线程:

创建线程需要调用pthread_create函数。该函数的原型如下:

int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
参数类型描述
threadpthread_t *用于存储新线程标识符的指针
attrconst pthread_attr_t *用于指定新线程的属性,如栈大小、调度策略等,可以为 NULL,表示使用默认属性
start_routinevoid *(*)(void *)新线程的起始函数,需要返回 void 指针类型的结果,并且带有一个 void 指针类型的参数
argvoid *传递给新线程起始函数的参数,可以为 NULL
返回值int0 表示成功,非 0 表示失败,错误代码保存在 errno

🚩 注意:在调用 pthread_create() 函数之后,新线程的执行与调用线程并行进行,它们之间没有特定的执行顺序。

下面是一个创建线程的例子:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,arg=%d, i=%d\n", *(int *)arg, i);
        sleep(1);
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t tid; // 线程标识符
    int arg = 123; // 传递给线程函数的参数

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, &arg) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 等待线程结束并回收资源
    if (pthread_join(tid, NULL) != 0) {
        printf("线程回收失败!\n");
        return 1;
    }

    printf("线程结束!\n");
    return 0;
}

图片3

三、 线程的终止

线程的终止有两种方式:自然终止强制终止

线程的自然终止是指线程执行完它的工作后自动退出,而强制终止是指在程序运行过程中,主线程或其他线程显式地终止一个正在运行的线程。

线程自然终止

线程可以通过调用pthread_exit函数来实现自然终止。pthread_exit函数的原型如下:

void pthread_exit(void *retval);

pthread_exit函数 无返回值,其中,参数retval是线程的退出状态,可以通过pthread_join函数获取。

下面是一个简单的例子,演示如何使用pthread_exit函数终止一个线程:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,i=%d\n", i);
        sleep(1);
    }
    pthread_exit((void *) "线程正常结束!");
}

int main()
{
    pthread_t tid; // 线程标识符

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, NULL) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 等待线程结束并回收资源
    void *retval;
    if (pthread_join(tid, &retval) != 0) {
        printf("线程回收失败!\n");
        return 1;
    }

    printf("%s\n", (char *)retval);
    printf("线程结束!\n");
    return 0;
}

上面的示例程序中,我们在线程函数中调用pthread_exit函数来终止线程,并返回一个字符串作为退出状态。

在主线程中,我们使用pthread_join函数等待线程结束,并通过指针retval获取线程的退出状态。

线程强制终止

在Linux中,线程的强制终止可以使用pthread_cancel函数来实现。pthread_cancel函数的原型如下:

int pthread_cancel(pthread_t thread);

其中,参数thread是要取消的线程标识符。当pthread_cancel函数被调用时,被取消的线程将立即退出。

参数类型描述
threadpthread_t要取消的线程标识符
返回值int0 表示成功,非 0 表示失败,错误代码保存在 errno

🚩注意:调用 pthread_cancel() 函数只是向指定线程发送一个取消请求,让指定线程尽快退出执行,而不会立即终止它的执行。

线程在接收到取消请求后,可以通过调用 pthread_setcancelstate() pthread_setcanceltype() 函数来指定如何响应请求,这里不再展开说明。

下面是一个简单的例子,演示如何使用pthread_cancel函数强制终止一个线程:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,i=%d\n", i);
        sleep(1);
    }
    pthread_exit((void *) "线程正常结束!");
}

int main()
{
    pthread_t tid; // 线程标识符

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, NULL) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 等待一段时间后强制终止线程
    sleep(2);
    if (pthread_cancel(tid) != 0) {
        printf("线程取消失败!\n");
        return 1;
    }

    // 等待线程结束并回收资源
    void *retval;
    if (pthread_join(tid, &retval) != 0) {
        printf("线程回收失败!\n");
        return 1;
    }

    if (retval == PTHREAD_CANCELED) {
        printf("线程被取消!\n");
    } else {
        printf("%s\n", (char *)retval);
    }
    printf("线程结束!\n");
    return 0;
}

上面的示例程序中,主线程调用了pthread_cancel函数,强制终止了子线程。

在子线程函数中,我们使用pthread_exit函数返回了一个字符串,如果子线程正常结束,那么在主线程中打印出来的将是这个字符串;如果子线程被强制终止,那么在主线程中打印出来的将是线程被取消!

图片4

  • 🙈不是这个啦
  • 我是想说,前面好几次都提到了线程回收, 你是不是忘了告诉我了
  • 👧 不好意思哈,一下子没忍住就说出来了

四、线程的回收

使用pthread_join函数等待线程结束。该函数需要两个参数:线程标识符和指向线程返回值的指针。

int pthread_join(pthread_t thread, void **value_ptr);
参数类型描述
threadpthread_t要等待的线程标识符
value_ptrvoid **用于获取线程的退出状态的指针,可以为 NULL,表示不关心退出状态
返回值int0 表示成功,非 0 表示失败,错误代码保存在 errno

🚩 注意:调用 pthread_join() 函数会阻塞当前线程,直到指定的线程终止为止。

如果指定的线程已经终止,那么该函数会立即返回,并且不会阻塞。

另外,线程的退出状态只有在 pthread_join() 调用成功时才能被获取,否则 value_ptr 指向的值是未定义的。

图片5

如果线程终止后,其它线程没有调用 pthread_join()函数来回收该线程,这个线程会变成僵尸线程,会浪费系统资源;若僵尸线程积累过多,那么会导致应

用程序无法创建新的线程。

图片6

五、线程的分离

可以使用pthread_detach函数将线程分离。pthread_detach函数的原型如下:

int pthread_detach(pthread_t thread);
参数类型描述
threadpthread_t要分离的线程标识符
返回值int0 表示成功,非 0 表示失败,错误代码保存在 errno 中

调用 pthread_detach() 函数将使得指定线程在退出时自动释放其相关资源,而不需要其他线程调用 pthread_join() 函数来等待它的退出并回收资源。

如果指定的线程已经被分离或者已经退出,那么调用 pthread_detach() 函数将返回一个错误。

下面是一个简单的例子,演示如何使用pthread_detach函数将线程分离:

#include <stdio.h>
#include <pthread.h>

void *thread_func(void *arg)
{
    int i;
    for (i = 0; i < 5; i++) {
        printf("这是线程函数,i=%d\n", i);
        sleep(1);
    }
    pthread_exit((void *) "线程正常结束!");
}

int main()
{
    pthread_t tid; // 线程标识符

    // 创建新线程
    if (pthread_create(&tid, NULL, thread_func, NULL) != 0) {
        printf("线程创建失败!\n");
        return 1;
    }

    // 分离线程
    if (pthread_detach(tid) != 0) {
        printf("线程分离失败!\n");
        return 1;
    }

    printf("线程已经分离,将自动回收资源!\n");

    // 程序结束
    return 0;
}

上面的示例程序中,我们在创建线程之后立即将线程分离,并打印一条提示信息,告诉用户线程已经分离,将在退出时自动回收资源。

当运行上面的程序时,可以看到如下输出:

线程已经分离,将自动回收资源!
这是线程函数,i=0
这是线程函数,i=1
这是线程函数,i=2
这是线程函数,i=3
这是线程函数,i=4

可以看到,程序创建了一个新线程,并立即将它分离。在子线程中,我们打印了5个字符串,每个字符串间隔1秒。

在主线程中,我们打印了一条提示信息,告诉用户线程已经分离,将在退出时自动回收资源。最后,程序正常结束,没有调用pthread_join函数。

小结

我们已经介绍了Linux线程的创建、终止、回收、分离等基本操作。

在实际编程中,我们可能还需要使用一些其他的函数和技巧,例如互斥锁、条件变量、信号量、读写锁等

图片7

👧 欲知后事如何,请听下回分解!


📢欢迎各位 👍点赞 ⭐收藏 📝评论,如有错误请留言指正,非常感谢!

### Linux 线程创建回收的性能开销 在Linux环境中,线程作为轻量级进程存在,其创建和销毁操作相较于传统进程更加高效。然而,在高发场景下,频繁创建和销毁线程仍然会产生显著的性能开销。 #### 创建线程的性能开销 当调用`pthread_create()`函数来启动一个新的线程时,内核需要执行一系列初始化工作,包括但不限于: - 分配栈空间给新线程; - 初始化线程控制块(TCB),记录线程状态和其他必要信息; - 设置上下文环境以便于调度器管理; 这些准备工作虽然相对简单快速,但在极高频率的操作中累积起来也会成为瓶颈[^1]。 对于多处理器或多核心系统而言,如果应用程序能够有效地利用硬件资源,则可以减轻部分因线程创建所带来的负担。但是需要注意的是,过度依赖增加线程数量可能会导致竞争条件加剧以及缓存失效等问题的发生[^2]。 #### 回收线程的性能开销 在线程完成任务之后,通常有两种方式处理该线程:等待它自然终止通过`pthread_join()`收集返回值;或者将其标记为可分离(separable),让操作系统自动清理不再使用的资源。前者涉及到阻塞当前执行路径直到目标线程结束,而后者则可能造成僵尸线程(zombie thread)现象——即已经退出但尚未被父进程释放的相关结构体仍占用着少量内存区域。 无论是哪种情况,都不可避免地会消耗一定的CPU周期用于管理和协调各个活动单元之间的关系变化过程。特别是在大规模行计算场合里,不当的设计模式很容易引发严重的效率损失甚至死锁状况出现。 ### 性能优化建议 为了降低上述提到的各种潜在负面影响,提高整体运行效能,可以从以下几个方面入手进行针对性改进措施: - **合理规划线程池大小**: 预先设定好固定数目的一组工作者线程(workers threads),按需分配具体工作任务而不是临时新建实例对象。这样既能保证响应速度又能有效防止无谓的增长趋势。 - **采用异步I/O模型**: 对于涉及大量磁盘读写或网络通信的应用程序来说,应该尽可能多地采纳非阻塞性设计思路,使得单个线程能够在等待外部事件的同时继续从事其他有意义的工作项。 - **调整优先级策略**: 根据实际业务逻辑需求灵活配置不同类别作业的重要性级别,确保关键流程获得足够的计算能力支持而不至于受到次要因素干扰影响进度安排。 - **优化同步机制**: 尽量减少临界区(critical section)范围内的争用程度,推广使用细粒度加锁(locking at finer granularity)技术手段以提升吞吐率表现水平。 - **考虑NUMA架构特性**: 如果服务器平台具备Non-uniform Memory Access (NUMA) 特征的话,那么应当特别关注如何使各子系统的交互更为顺畅和谐,比如通过绑定特定物理位置上的CPU核心集簇去专门负责某些类型的负载请求等办法实现局部最优解方案的选择[^3]. ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> void *thread_function(void *arg){ printf("Thread is running\n"); sleep(1); pthread_exit(NULL); // Properly exit the thread to avoid zombie state. } int main(){ pthread_t tid; int ret; ret = pthread_create(&tid, NULL, thread_function, NULL); if(ret != 0){ perror("Failed to create thread"); exit(EXIT_FAILURE); } void* res; ret = pthread_join(tid,&res); // Wait for the created thread and collect its result. if(ret != 0){ perror("Failed to join with thread"); exit(EXIT_FAILURE); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值