• 主线程调用return和pthread_exit有什么区别?


    子线程还没结束,主线程调用return

    #include 
    #include 
    #include 
    #include  
    
    void* fun(void *arg){
    	sleep(30);
    	printf("%ld thread exit\n", syscall(SYS_gettid)); // syscall(SYS_gettid)返回轻量级进程LWP的id,相当于内核线程ID
    	pthread_exit(0);
    }
    
    int main(){
    	pthread_t tid;
    	int res = pthread_create(&tid, NULL, fun, NULL);
    	printf("main pid = %d\n", getpid());
    	
    	sleep(20);
    	printf("main pthread_exit\n");
    	
        return 0;  // 就是调用exit(0)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们启动程序,看到该进程号为17428,我们使用watch -n 1 ps -Lf 17428每隔1s刷新一次该命令,查看17428的线程信息

    在这里插入图片描述

    我们看到main调用return后,两个线程一起退出了

    在这里插入图片描述

    子线程还没结束,主线程调用pthread_exit

    #include 
    #include 
    #include 
    #include  
    
    void* fun(void *arg){
    	sleep(30);
    	printf("%ld thread exit\n", syscall(SYS_gettid)); // syscall(SYS_gettid)返回轻量级进程LWP的id
    	pthread_exit(0);
    }
    
    int main(){
    	pthread_t tid;
    	int res = pthread_create(&tid, NULL, fun, NULL);
    	printf("main pid = %d\n", getpid());
    	
    	sleep(20);
    	printf("main pthread_exit\n");
    	pthread_exit(0);
        // return 0;  // 就是调用exit(0)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    我们启动程序,看到该进程号为16266,我们使用watch -n 1 ps -Lf 16266每隔1s刷新一次该命令,查看16266的线程信息

    在这里插入图片描述
    当main线程调用pthread_exit后,就成为僵尸线程了,其实也是僵尸进程,linux下并没有真正意义上的线程存在,linux中使用进程来模拟实现线程,父进程创建子进程,子进程执行父进程的一部分代码,并且与父进程共享同一个地址空间。这些一个一个被创建出来的子进程可看做线程,这种线程也称之为轻量级进程。

    main线程结束了,此时子线程还在正常运行。一般来说,不建议这么做,子线程退出后,主线程可以等待回收;但是子线程不会去回收主线程,所以主线程退出的时候,由于没有人回收,这个时候,主线程就会出现类似于“僵尸进程”的情况

    在这里插入图片描述
    子线程正常退出,线程全部退出,进程销毁

    在这里插入图片描述

    分析:

    main函数的return,相当于是在线程中调用了系统函数exit,其他函数调用return不会触发exit(任何线程半路调用exit,都会导致进程终止),表示直接终止了整个进程,那么操作系统会把当前进程所有的资源,包括内存、io、线程、管道、fd等全部终止使用并且回收,相当于进程啥都没了

    pthread_exit,仅仅只是结束当前线程,进程地址空间还在,所有的资源也都在,其他线程可以正常使用进程地址空间

    普通thread在main线程结束前,一定要join thread,保证业务执行的完整性; detach thread不需要,一般detach thread设计的目的,就是在后台做一些非关键性任务,是否正常结束并没有要求,也不会出什么错误,如果要求任务的执行必须是完整的,不能设计成detach thread

    主线程使用pthread_join,等待子线程结束,回收子线程资源

    #include 
    #include 
    #include 
    #include 
    #include  
    
    void* fun(void *arg){
    	sleep(20);
    	printf("%ld thread exit\n", syscall(SYS_gettid));
    	pthread_exit(0);
    }
    
    int main(){
    	pthread_t tid;
    	int res = pthread_create(&tid, NULL, fun, NULL);
    	printf("main pid = %d\n", getpid());
    	
    	sleep(10);
    
    	pthread_join(tid, NULL); // 第二个参数是void*,用于接收线程函数的返回值
    	printf("main pthread_exit\n");
    	
        return 0;  // 就是调用exit(0)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    pthread_detach,系统自动回收子线程资源

    int pthread_detach(pthread_t tid);  // 成功返回0,失败返回error number
    
    • 1

    pthread_detach后的线程结束后,其退出状态不由其他线程获取,而是由系统获取后释放资源,这种detach线程不会产生僵尸进程

    pthread_detach()和pthread_join()就是控制子线程回收资源的两种不同的方式。同一进程间的线程具有共享和独立的资源,其中共享的资源有堆、全局变量、静态变量、文件等公用资源。而独享的资源有栈和寄存器,这两种方式就是决定子线程结束时如何回收独享的资源

    • 如果是joinable状态,则该线程结束后(通过pthread_exit结束或者线程执行体任务执行完毕)不会释放线程所占用堆栈和线程描述符(总计8K多)等资源,除非在主线程调用了pthread_join函数之后才会释放(如果不调用pthread_join,那就只有进程终止时,才能回收线程资源了)。pthread_join函数一般应用在主线程需要等待子线程结束后才继续执行的场景,pthread_join是一个阻塞函数,调用方会阻塞到pthread_join所指定的tid的线程结束后才被回收。这跟子进程很相似,如果不用父进程wait回收的话,就会变成僵尸进程;同理,如果一个可结合态线程不用pthread_join回收,则会变成类似僵尸进程

    • 如果是unjoinable状态,即detach后的线程,它的资源在它终止时由系统自动释放。实现方式是在创建时指定属性,或者在线程执行体的最开始处添加一行:pthread_detach(pthread_self());不会阻塞,调用它后,线程运行结束后会自动释放资源,后者非常方便

    总结:

    pthread_detach()即主线程与子线程分离,两者相互不干涉,但是分离线程还是使用所属进程的地址空间,如果主线程调用exit,会导致进程终止,所有的资源都被回收,分离线程也会被销毁。分离线程不能再join,否则报错

    pthread_join()即是子线程合入主线程,主线程会一直阻塞,直到子线程执行结束,然后回收子线程资源,并继续执行

  • 相关阅读:
    有偿找proteus51单片机仿真代做
    【Systemctl 启动Java程序但开机未自启动问题】
    MySql——InnoDB引擎总体架构
    RSA的一些数论知识
    vue组件之间的五种传值方法(父子\兄弟\跨组件)
    前端体验优化(3)——后端
    如何写出优雅的代码?试试这些开源项目「GitHub 热点速览」
    Python学习第三天(列表常用函数)
    Vue封装全局SVG组件
    LinkedList 源码解析(JDK1.8)
  • 原文地址:https://blog.csdn.net/qq_42500831/article/details/126927526