pthread 的 joinable 和 detached

对于多线程程序,在程序开始运行后,会首先建立一个主线程,用于执行程序。在程序执行过程中,可能由于需要,会建立新的线程,用于执行并行操作。这时建立的线程称为子线程。
除去主线程外,其他所有的线程都属于子线程,为平等关系,即使是由子线程建立的子线程。

在 POSIX Threads 中,有两种线程类型:

  • 结合(joinable):对于结合的线程,主线程需要用join()函数来等待子线程执行完毕获得返回值,并释放资源;
  • 分离(detached):分离的线程相对而言独立于主线程,不需要主线程等待,会自动释放资源;joinable 的线程可以转换成 detached 的线程,但不能反向转换;

尽管一些文章会提到 joinable 线程在除非关机或重启都不会释放资源1,但是对于目前的操作系统的智能程度,在进程结束后都会被清除资源2,joinable 线程和 detached 线程的区别在于:

  • 主线程是否需要等待线程执行结束
  • 是否需要立即释放资源

一个更为直观的类比是,joinable 是类似于常见的 Python 线程模型,创建后开启线程,并且使用 join 来等待所有线程执行完成。当然,如果主线程不需要等待子线程执行结束就可以执行其他任务,也可以不使用 join 。这时效果上类似于 detached 线程模型3

而 detached 线程模型则很类似于 Go 语言的 goroutine 。子线程执行后,主线程就不再管子线程的事情,完全由子线程自行处理调度问题。(当然, Go 中可以使用 selectchannel 来实现等待任务执行结束,或者任务之间数据交互)

joinable 线程样例

执行下面的程序,会开启 THREAD_NUM 个线程,并在主线程等待其执行完毕

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

#define THREAD_NUM 3

void *thread_function(void *arg) {
    int tid = *(int *)arg;
    for (int i = 0; i < 3; i++) {
        printf("%d-%d\n", tid, i);
        sleep(1);
    }
    return NULL;
}

int main(void) {
    pthread_t *threads = malloc(THREAD_NUM * sizeof(pthread_t));

    for (int i = 0; i < THREAD_NUM; ++i) {
        if (pthread_create(&threads[i], NULL, thread_function, &i)) {
            printf("error creating thread.");
            abort();
        }
    }

    for (int i = 0; i < THREAD_NUM; ++i) {
        if (pthread_join(threads[i], NULL)) {
            printf("error join thread.");
            abort();
        }
    }

    printf("thread done! \n");
    return 0;
}

detached 线程样例

执行下面的程序,会开启 THREAD_NUM 个线程,并在主线程等待 2 秒(因为子线程需要 3 秒才能输出完毕,因此不会输出所有内容。

detached 线程很适合主线程在无限循环或者在运行很长的服务,同时有很多个需要并行执行的任务(一般不需要将执行信息反馈到创建的线程,当然也可以使用互斥锁等方法来在线程间同步数据)

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

#define THREAD_NUM 3

void *thread_function(void *arg) {
    int tid = *(int *)arg;
    for (int i = 0; i < 3; i++) {
        printf("%d-%d\n", tid, i);
        sleep(1);
    }
    return NULL;
}

int main(void) {
    pthread_t *threads = malloc(THREAD_NUM * sizeof(pthread_t));

    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    for (int i = 0; i < THREAD_NUM; ++i) {
        if (pthread_create(&threads[i], &attr, thread_function, &i)) {
            printf("error creating thread.");
            abort();
        }
    }

    sleep(2);

    printf("thread done! \n");
    exit(0);
}

参考资料


  1. c++11中thread join和detach的区别 ↩︎

  2. Detached vs. Joinable POSIX threads ↩︎

  3. python threading 一定要 join 吗? ↩︎