当主线程退出时,其它线程是否也会退出?

31

我有一个关于同一进程中主线程和其他线程的问题。当主函数返回时,其他线程是否也退出?我对此感到困惑。

请看以下测试代码:

void* test1(void *arg)
{
    unsigned int i = 0;
    while (1){
        i+=1;
    }
    return NULL;
}

void* test2(void *arg)
{
    long double i = 1.0;
    while (1){
        i *= 1.1;
    }
    return NULL;
}

void startThread ( void * (*run)(void*), void *arg) {
  pthread_t t;
  pthread_attr_t attr;
  if (pthread_attr_init(&attr) != 0
      || pthread_create(&t, &attr, run, arg) != 0
      || pthread_attr_destroy(&attr) != 0
      || pthread_detach(t) != 0) {
    printf("Unable to launch a thread\n");
    exit(1);
  }
}

int main()
{
    startThread(test1, NULL);
    startThread(test2, NULL);

    sleep(4);
    printf("main thread return.\n");

    return 0;
}

当"main thread return."打印出来时,线程test1和test2也退出了,请问这是为什么?

问题的文本与问题主体不符。这是关于主线程退出时发生的情况吗?还是关于主线程从main函数返回时发生的情况?(显然,线程如果退出,就不能返回。它只能做其中之一。) - David Schwartz
4个回答

29

对于每个新线程,您应该使用pthread_join()来告知调用线程等待子线程的终止,暂停执行-和进程退出-直到这些线程终止。

在创建的线程上调用pthread_detach并不能保持它们在进程退出后仍然存在。根据linux man页面:

分离属性仅确定系统在线程终止时的行为;如果进程使用exit(3)(或等效地,如果主线程返回),它不会阻止线程被终止。

有时会看到在main中使用pthread_exit代替显式的pthread_join调用,意图是以这种方式退出main将允许其他线程继续运行。实际上,linux man page明确指出了这一点:

为了让其他线程继续执行,主线程应该通过调用pthread_exit()而不是exit(3)来终止。

但我不知道这是否是在所有平台上预期的行为,我一直坚持使用pthread_join

pthread_join需要目标线程的pthread_t,因此您的代码需要稍作更改,因为您需要在调用pthread_join等待两个线程之前创建这两个线程。因此,您不能在startThread中调用它。您需要返回一个pthread_t或将指向pthread_t的指针传递给您的startThread函数。


3
仔细阅读pthread_detach的文档。它并不像你想象中的那样工作。 - pb2q
@laifjei 也许你把detach和Java/C#/Python等语言中提供的守护线程功能混淆了。Java在任何非守护线程仍然存活时会保持进程处于活动状态(即使主线程已经死亡)。 - Kuba Beránek

29

当主线程返回时(即从main函数返回),它会终止整个进程,包括所有其他线程。当调用exit时也会发生相同的情况。通过调用pthread_exit可以避免这种情况。

pthread_detach的目的是为了使您无需加入其他线程即可释放它们的资源。将线程分离不会使其在进程终止后仍然存在,它仍将与所有其他线程一起被销毁。


26
这个回答的原文不正确。当主线程(或任何线程)调用 exit,或者初始的 main 函数调用返回时,整个进程都会退出。但是主线程可以使用 pthread_exit 退出而不影响其他线程。 - R.. GitHub STOP HELPING ICE
@R.. 这是否意味着一个进程可以退出,但它的线程继续执行? - TonySalimi
@hsalimi:进程退出后线程不可能继续运行。R说主线程可以通过pthread_exit退出,而其他线程可以继续运行。 - Dietrich Epp
@hsalimi:不,线程是进程的一部分。如果进程退出,它们以及所有其他进程资源都将停止存在。通过pthread_exit终止初始线程并不会导致进程退出。 - R.. GitHub STOP HELPING ICE

1
当您从main()返回时,您的进程中的所有线程都将终止。 libc库负责通过在main()函数返回时调用exit()来实现此行为。反过来,exit()函数最终会调用一个名为_exit()的薄包装器函数(从libc v2.3开始),该函数将调用exit_group系统调用并结束您的进程并终止其所有线程。
这个最后的系统调用就是导致您注意到的行为的原因。
我们可以在_exit()手册here中看到这个微妙的说明。
C library/kernel differences
       In glibc up to version 2.3, the _exit() wrapper function invoked
       the kernel system call of the same name.  Since glibc 2.3, the
       wrapper function invokes exit_group(2), in order to terminate all
       of the threads in a process.

如果您的意图是避免这种行为,唯一的选择就是调用pthread_exit,它将结束您的主线程并防止您返回到libc__libc_start_main()函数。

-1
顺便提一下同一个话题,我想指出《高级程序设计环境》(Stevens & Rago)第三版中似乎有一个错误。在第7.3节中,它说“正常终止有五种方式”,其中之一是“从最后一个线程调用pthread_exit(第11.5节)”。我认为这是不正确的,因为要么主函数仍在运行(而pthread_exit不会终止程序,因为主函数会完成这个任务),要么主函数已经返回,从而终止了程序,正如本文所正确指出的那样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接