当前位置 主页 > 服务器问题 > Linux/apache问题 >

    简单了解C语言中主线程退出对子线程的影响

    栏目:Linux/apache问题 时间:2019-12-18 09:22

    这篇文章主要介绍了简单了解C语言中主线程退出对子线程的影响,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    对于程序来说,如果主进程在子进程还未结束时就已经退出,那么Linux内核会将子进程的父进程ID改为1(也就是init进程),当子进程结束后会由init进程来回收该子进程。

    那如果是把进程换成线程的话,会怎么样呢?假设主线程在子线程结束前就已经退出,子线程会发生什么?

    在一些论坛上看到许多人说子线程也会跟着退出,其实这是错误的,原因在于他们混淆了线程退出和进程退出概念。实际的答案是主线程退出后子线程的状态依赖于它所在的进程,如果进程没有退出的话子线程依然正常运转。如果进程退出了,那么它所有的线程都会退出,所以子线程也就退出了。

    主线程先退出

    先来看一个主线程先退出的例子:

    #include <pthread.h>
    #include <unistd.h>
    
    #include <stdio.h>
    
    void* func(void* arg)
    {
      pthread_t main_tid = *static_cast<pthread_t*>(arg);
      pthread_cancel(main_tid);
      while (true)
      {
        //printf("child loops\n");
      }
      return NULL;
    }
    
    int main(int argc, char* argv[])
    {
      pthread_t main_tid = pthread_self();
      pthread_t tid = 0;
      pthread_create(&tid, NULL, func, &main_tid);
      while (true)
      {
        printf("main loops\n");
      }
      sleep(1);
      printf("main exit\n");
      return 0;
    }

    把主线程的线程号传给子线程,在子线程中通过pthread_cancel终止主线程使其退出。运行程序,可以发现在打印了一定数量的「main loops」之后程序就挂起了,但却没有退出。

    主线程因为被子线程终止了,所有没有看到「main exit」的打印。子线程终止了主线程后进入了死循环while中,所以程序看起来像挂起了。如果我们让子进程while循环中的打印语句生效再运行就可以发现程序会一直打印「child loops」字样。

    主线程被子线程终止了,但他们所依赖的进程并没有退出,所以子线程依然正常运转。

    主线程随进程一起退出

    之前看到一些人说如果主线程先退出了,子线程也会跟着退出,其实他们混淆了线程退出和进程退出的概念。下面这个例子代表了他们的观点:

    void* func(void* arg)
    {
      while (true)
      {
        printf("child loops\n");
      }
      return NULL;
    }
    
    int main(int argc, char* argv[])
    {
      pthread_t main_tid = pthread_self();
      pthread_t tid = 0;
      pthread_create(&tid, NULL, func, &main_tid);
      sleep(1);
      printf("main exit\n");
      return 0;
    }

    运行上面的代码,会发现程序在打印一定数量的「child loops」和一句「main exit」之后退出,并且在退出之前的最后一句打印是「main exit」。

    按照他们的逻辑,你看,因为主线程在打印完「main exit」后退出了,然后子线程也跟着退出了,所以随后就没有子线程的打印了。

    但其实这里是混淆了进程退出和线程退出的概念了。实际的情况是主线程中的main函数执行完ruturn后弹栈,然后调用glibc库函数exit,exit进行相关清理工作后调用_exit系统调用退出该进程。所以,这种情况实际上是因为进程运行完毕退出导致所有的线程也都跟着退出了,并非是因为主线程的退出导致子线程也退出。

    Linux线程模型

    实际上,posix线程和一般的进程不同,在概念上没有主线程和子线程之分(虽然在实际实现上还是有一些区分),如果仔细观察apue或者unp等书会发现基本看不到「主线程」或者「子线程」等词语,在csapp中甚至都是用「对等线程」一词来描述线程间的关系。