Linux线程(4)——pthread_detach()自动回收线程资源

这篇具有很好参考价值的文章主要介绍了Linux线程(4)——pthread_detach()自动回收线程资源。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

分离线程        

        默认情况下,当线程终止时,其它线程可以通过调用 pthread_join()获取其返回状态、回收线程资源,有时,程序员并不关心线程的返回状态,只是希望系统在线程终止时能够自动回收线程资源并将其移除。在这种情况下,可以调用 pthread_detach()将指定线程进行分离,也就是分离线程,pthread_detach()函数原型如下所示:

#include <pthread.h>

int pthread_detach(pthread_t thread);

        使用该函数需要包含头文件,参数 thread 指定需要分离的线程,函数 pthread_detach()调用成功将返回 0;失败将返回一个错误码。

        一个线程既可以将另一个线程分离,同时也可以将自己分离,譬如:

pthread_detach(pthread_self());

        一旦线程处于分离状态,就不能再使用 pthread_join()来获取其终止状态,此过程是不可逆的,一旦处于分离状态之后便不能再恢复到之前的状态。处于分离状态的线程,当其终止后,能够自动回收线程资源。

使用示例

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

static void *new_thread_start(void *arg){
    int ret;
    /* 自行分离 */
    ret = pthread_detach(pthread_self());
    if (ret) {
       fprintf(stderr, "pthread_detach error: %s\n", strerror(ret));
       return NULL;
    }
    printf("新线程 start\n");
    sleep(2); //休眠 2 秒钟
    printf("新线程 end\n");
    pthread_exit(NULL);
}

int main(void){
    pthread_t tid;
    int ret;

    /* 创建新线程 */
    ret = pthread_create(&tid, NULL, new_thread_start, NULL);
    if (ret) {
        fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
        exit(-1);
    }
    sleep(1); //休眠 1 秒钟

    /* 等待新线程终止 */
    ret = pthread_join(tid, NULL);
    if (ret)
        fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
    pthread_exit(NULL);
}

        示例代码中,主线程创建新的线程之后,休眠 1 秒钟,调用 pthread_join()等待新线程终止;新线程调用 pthread_detach(pthread_self())将自己分离,休眠 2 秒钟之后 pthread_exit()退出线程;主线程休眠 1 秒钟是能够确保调用 pthread_join()函数时新线程已经将自己分离了,所以按照上面的介绍可知,此时主线程调用 pthread_join()必然会失败,测试结果如下:pthread_detach,Linux,linux

         打印结果正如我们所料,主线程调用 pthread_join()确实会出错,错误提示为“Invalid argument”。

注册线程清理处理函数

        之前学习进程的时候,我们学习了 atexit()函数,使用 atexit()函数注册进程终止处理函数,当进程调用 exit()退出时就会执行进程终止处理函数;其实,当线程退出时也可以这样做,当线程终止退出时,去执行这样的处理函数, 我们把这个称为线程清理函数(thread cleanup handler)。

        与进程不同,一个线程可以注册多个清理函数,这些清理函数记录在栈中,每个线程都可以拥有一个清理函数栈,栈是一种先进后出的数据结构,也就是说它们的执行顺序与注册(添加)顺序相反,当执行完所有清理函数后,线程终止。

        线程通过函数 pthread_cleanup_push()和 pthread_cleanup_pop()分别负责向调用线程的清理函数栈中添加和移除清理函数,函数原型如下所示:

#include <pthread.h>

void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

        调用 pthread_cleanup_push()向清理函数栈中添加一个清理函数,第一个参数 routine 是一个函数指针, 指向一个需要添加的清理函数,routine()函数无返回值,只有一个 void *类型参数;第二个参数 arg,当调用清理函数 routine()时,将 arg 作为 routine()函数的参数。

        既然有添加,自然就会伴随着删除,就好比入栈和出栈,调用函数 pthread_cleanup_pop()可以将清理函数栈中最顶层(也就是最后添加的函数,最后入栈)的函数移除。

        当线程执行以下动作时,清理函数栈中的清理函数才会被执行:

  1. 线程调用 pthread_exit()退出时;
  2. 线程响应取消请求时;
  3. 用非 0 参数调用 pthread_cleanup_pop()

        除了以上三种情况之外,其它方式终止线程将不会执行线程清理函数,譬如在线程 start 函数中执行 return 语句退出时不会执行清理函数。

        函数 pthread_cleanup_pop()的 execute 参数,可以取值为 0,也可以为非 0;如果为 0,清理函数不会被调用,只是将清理函数栈中最顶层的函数移除;如果参数 execute 为非 0,则除了将清理函数栈中最顶层的函数移除之外,还会清理该函数。

        尽管我们将 pthread_cleanup_push() 和 pthread_cleanup_pop()称之为函数,但它们是通过宏来实现, 可展开为分别由{和}所包裹的语句序列,所以必须在与线程相同的作用域中以匹配对的形式使用,必须一一对应着来使用,譬如:

pthread_cleanup_push(cleanup, NULL);
pthread_cleanup_push(cleanup, NULL);
pthread_cleanup_push(cleanup, NULL);
......
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);

使用示例

        上文给出了一个使用线程清理函数的例子(虽然例子并没有什么实际作用),但它描述了其中所涉及到的清理机制。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

static void cleanup(void *arg){
    printf("cleanup: %s\n", (char *)arg);
}

static void *new_thread_start(void *arg){
    printf("新线程--start run\n");
    pthread_cleanup_push(cleanup, "第 1 次调用");
    pthread_cleanup_push(cleanup, "第 2 次调用");
    pthread_cleanup_push(cleanup, "第 3 次调用");
    sleep(2);
    pthread_exit((void *)0); //线程终止

    /* 为了与 pthread_cleanup_push 配对,不添加程序编译会通不过 */
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
}

int main(void)
{
    pthread_t tid;
    void *tret;
    int ret;
    
    /* 创建新线程 */
    ret = pthread_create(&tid, NULL, new_thread_start, NULL);
    if (ret) {
        fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
        exit(-1);
    }
    
    /* 等待新线程终止 */
    ret = pthread_join(tid, &tret);
    if (ret) {
        fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
        exit(-1);
    }
    printf("新线程终止, code=%ld\n", (long)tret);
    exit(0);
}

        主线程创建新线程之后,调用 pthread_join()等待新线程终止;新线程调用 pthread_cleanup_ push()函数添加线程清理函数,调用了三次,但每次添加的都是同一个函数,只是传入的参数不同;清理函数添加完成, 休眠一段时间之后,调用 pthread_exit()退出。之后还调用了 3 次 pthread_cleanup_pop(),在这里的目的仅仅只是为了与 pthread_cleanup_push()配对使用,否则编译不通过。接下来编译运行:

pthread_detach,Linux,linux

         从打印结果可知,先添加到线程清理函数栈中的函数后执行,添加顺序与执行顺序相反。

        将新线程中调用的 pthread_exit()替换为 return,在进行测试,发现并不会执行清理函数。 有时在线程功能设计中,线程清理函数并不一定需要在线程退出时才执行,譬如当完成某一个步骤之 后,就需要执行线程清理函数,此时我们可以调用 pthread_cleanup_pop()并传入非 0 参数,来手动执行线程清理函数,示例代码如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

static void cleanup(void *arg){
 printf("cleanup: %s\n", (char *)arg);
}

static void *new_thread_start(void *arg){
    printf("新线程--start run\n");
    pthread_cleanup_push(cleanup, "第 1 次调用");
    pthread_cleanup_push(cleanup, "第 2 次调用");
    pthread_cleanup_push(cleanup, "第 3 次调用");
    pthread_cleanup_pop(1); //执行最顶层的清理函数
    printf("~~~~~~~~~~~~~~~~~\n");
    sleep(2);
    pthread_exit((void *)0); //线程终止
    /* 为了与 pthread_cleanup_push 配对 */
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
}

int main(void){
    pthread_t tid;
    void *tret;
    int ret;
    /* 创建新线程 */
    ret = pthread_create(&tid, NULL, new_thread_start, NULL);
    if (ret) {
        fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
        exit(-1);
    }
    /* 等待新线程终止 */
    ret = pthread_join(tid, &tret);
    if (ret) {
        fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
        exit(-1);
    }
    printf("新线程终止, code=%ld\n", (long)tret);
    exit(0);
}

        上述代码中,在新线程调用 pthread_exit()之前,先调用 pthread_cleanup_pop(1)手动运行了最顶层的清理函数,并将其从栈中移除,测试结果:

pthread_detach,Linux,linux

         从打印结果可知,调用 pthread_cleanup_pop(1)执行了最后一次注册的清理函数,调用 pthread_exit()退出线程时执行了 2 次清理函数,因为前面调用 pthread_cleanup_pop()已经将顶层的清理函数移除栈中了,自然在退出时就不会再执行了。文章来源地址https://www.toymoban.com/news/detail-773841.html

到了这里,关于Linux线程(4)——pthread_detach()自动回收线程资源的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《Linux操作系统编程》 第十章 线程与线程控制: 线程的创建、终止和取消,detach以及线程属性

    🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬

    2024年02月11日
    浏览(80)
  • linux_设置线程属性-pthread_attr_t结构体-设置线程分离态-修改线程栈的大小-NPTL

    接上一篇:linux_线程分离pthread_detach函数-线程取消pthread_cancel函数-线程相等pthread_equal函数   本次来分享linux线程的属性设置,线程属性主要是一个结构体 pthread_attr_t ,这个结构体中的成员就是线程的属性了,需要通过一系列的函数来修改,话不多说,上菜: 此博主在CSD

    2024年02月02日
    浏览(30)
  • Linux网络编程:socket & pthread_create()多线程 实现clients/server通信

    UNIX网络编程:socket fork()多进程 实现clients/server通信 随笔介绍了通过fork()多进程实现了服务器与多客户端通信。但除了多进程能实现之外,多线程也是一种实现方式。 重要的是,多进程和多线程是涉及操作系统层次。随笔不仅要利用pthread_create()实现多线程编程,也要理解线

    2024年02月05日
    浏览(34)
  • 【Linux】C语言中多线程的创建、退出、回收、分离

    线程是轻量级的进程(LWP:light weight process),在 Linux 环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源,可以这样理解, 进程是资源分配的最小单位,线程是操

    2024年02月09日
    浏览(33)
  • C++11并发与多线程笔记(3)线程传参详解,detach()大坑,成员函数做线程函数

    在使用detach时,不推荐引用传递,指针传递肯定有问题 在创建线程的同时构造临时对象的方法传递参数是可行的 如果传递int这种基本数据类型,推荐使用 值传递 ,不要用引用 如果传递 类对象 ,避免使用隐式类型转换,全部都是创建线程这一行就创建出 临时对象 ,然后在

    2024年02月12日
    浏览(32)
  • Visual Studio (2022)安装配置pthread.h多线程库

    各位好,之前尝试用DEVC++编写多线程库问题时遇到报错,然后发现解决不了后转战Visual Studio。顺带分享一下安装方法。 首先是pthread的下载网站:https://www.mirrorservice.org/sites/sourceware.org/pub/pthreads-win32/ 选择最新的(最下面)那个版本下载。 Windows选下面那个  解压完成后我们会

    2024年02月08日
    浏览(38)
  • 【Linux C | 多线程编程】线程的连接、分离,资源销毁情况

    😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭 ⏰发布时间⏰:2024-04-01 14:52:46 本文未经允许,不得转发!!! 记住一句话,“创建线程后,要么连接该线程,要么使该线程分离,否则可能导致资源无法

    2024年04月13日
    浏览(27)
  • UNIX网络编程:socket & pthread_create()多线程 实现clients/server通信

    UNIX网络编程:socket fork()多进程 实现clients/server通信 随笔介绍了通过fork()多进程实现了服务器与多客户端通信。但除了多进程能实现之外,多线程也是一种实现方式。 重要的是,多进程和多线程是涉及操作系统层次。随笔不仅要利用pthread_create()实现多线程编程,也要理解线

    2024年02月06日
    浏览(41)
  • 【探索Linux】—— 强大的命令行工具 P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)

    在上一篇文章中,我们对多线程编程的基础知识进行了深入的探讨,包括了线程的概念、线程控制以及分离线程等关键点。通过这些内容的学习,我们已经能够理解并实现简单的多线程程序。然而,随着程序复杂度的提升,仅仅掌握这些基础是远远不够的。在多线程环境下,

    2024年02月05日
    浏览(30)
  • Linux pthread_create源码分析

    本文介绍pthread_create函数的使用和源码分析。 /include/pthread.h bionic/libc/bionic/pthread_create.cpp bionic/libc/bionic/pthread_attr.cpp Android中的绝大部分线程,最后都是通过pthread_create创建的。 pthread_t *thread: 传递一个 pthread_t 类型的指针变量,也可以直接传递某个 pthread_t 类型变量的地址。

    2024年02月07日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包