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 中可以使用 select
和 channel
来实现等待任务执行结束,或者任务之间数据交互)
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); }