线程的创建
原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数:
thread:线程ID存放空间地址
attr:线程属性,默认填NULL
start_routine:线程函数指针
arg:给线程函数传递的参数
测试例程:
1 #include <stdio.h>
2 #include <pthread.h>
3
4 void * pthread_func (void *p_arg)
5 {
6 while (1) {
7 printf("pthread arg = %d\n", (int)p_arg);
8
9 sleep(5);
10 }
11
12 return NULL;
13 }
14
15 int main(int argc, const char *argv[])
16 {
17 pthread_t id;
18
19 pthread_create(&id, NULL, pthread_func, (void *)6);
20
21 while (1) {
22 printf("main\n");
23
24 sleep(2);
25 }
26
27 return 0;
28 }
编译时输入:gcc main.c -lpthread
探究是否先创建的线程会先执行
如果创建了很多个线程,这些线程是怎么开始执行的呢,为此我写了如下代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 /* 定义最大线程数 */
5 #define THREAD_CNT 100
6
7 void * func(void *p_arg)
8 {
9 printf("%d ", (int)p_arg);
10 }
11
12 int main(int argc, const char *argv[])
13 {
14 int i;
15 pthread_t tid[THREAD_CNT];
16
17 for(i = 0; i < THREAD_CNT; i++)
18 {
19 pthread_create(&tid[i], NULL, func, (void *)i);
20 }
21
22 sleep(1);
23
24 return 0;
25 }
三次测试结果如下:
从结果上可以看出,并非先创建的线程会先执行,从结果上看不出规律。
线程退出后资源占用情况探究
如果说创建的线程不会退出,那就不会有这个问题,在下列几篇博客的启发下,我决定亲自来探究一下这个问题,但是使用的方法和工具也许会和他们有所区别。
多线程之pthread_create()函数:https://blog.csdn.net/wushuomin/article/details/80051295
解决使用pthread_create函数造成的内存泄露:https://blog.csdn.net/qiurisuixiang/article/details/6648213
linux中pthread_join()与pthread_detach()详解:https://blog.csdn.net/weibo1230123/article/details/81410241
探究的具体问题为,线程的分离属性与非分离属性在线程退出后的资源回收情况,以及调用不同的函数对结果的影响。
编写测试程序:
main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5 /* 定义最大线程数 */
6 #define THREAD_CNT 5
7
8 void * func(void *p_arg)
9 {
10 //pthread_detach(pthread_self());
11 printf("%d ", (int)p_arg);
12 }
13
14 int main(int argc, const char *argv[])
15 {
16 int i;
17 pthread_t tid[THREAD_CNT];
18 //pthread_attr_t attr;
19
20 //pthread_attr_init(&attr);
21 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
22 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
23
24 for(i = 0; i < THREAD_CNT; i++) {
25 pthread_create(&tid[i], NULL, func, (void *)i);
26 //pthread_join(tid[i], NULL);
27 //sleep(1);
28 }
29
30 // sleep(1);
31
32 return 0;
33 }
各种情况的验证都将在这份代码上进行改动。
首先用gcc main.c -lpthread生成a.out,然后执行valgrind ./a.out命令(如果没有valgrind需要先安装valgrind),结果如下:
这样可能发生内存泄漏,猜测可能是因为主进程退出而线程还未来得及退出导致的,因此放开代码第30行的注释,让线程有时间去退出,但是执行结果是一样的。
放开第26行的注释,每执行一个线程之后都用pthread_join回收,运行结果为 没有泄漏的可能,如下:
为了更直观的观察系统资源使用情况,将第6行THREAD_CNT的值改到1000,注释掉第26行,并放开第27行,如下:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5 /* 定义最大线程数 */
6 #define THREAD_CNT 1000
7
8 void * func(void *p_arg)
9 {
10 //pthread_detach(pthread_self());
11 printf("%d \n", (int)p_arg); //这里不加换行符的话数字打印不出来
12 }
13
14 int main(int argc, const char *argv[])
15 {
16 int i;
17 pthread_t tid[THREAD_CNT];
18 //pthread_attr_t attr;
19
20 //pthread_attr_init(&attr);
21 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
22 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
23
24 for(i = 0; i < THREAD_CNT; i++) {
25 pthread_create(&tid[i], NULL, func, (void *)i);
26 //pthread_join(tid[i], NULL);
27 sleep(1);
28 }
29
30 sleep(1);
31
32 return 0;
33 }
编译成功后直接运行a.out,然后另开一个终端,每隔一段时间运行ps -aux命令查看进程状态,并记录结果:
注意到名为"a.out"的进程,%MEM,VSZ和RSS一栏上的数字越来越大(%MEM表示内存使用率,RSS表示固定内存量,VSZ表示虚拟内存量)。
放开第10行的注释:pthread_detach(pthread_self()); 或者 放开第26行的注释:pthread_join(tid[i], NULL); 或者 将代码改成如下样子:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4
5 /* 定义最大线程数 */
6 #define THREAD_CNT 1000
7
8 void * func(void *p_arg)
9 {
10 //pthread_detach(pthread_self());
11 printf("%d \n", (int)p_arg);
12 }
13
14 int main(int argc, const char *argv[])
15 {
16 int i;
17 pthread_t tid[THREAD_CNT];
18 pthread_attr_t attr;
19
20 pthread_attr_init(&attr);
21 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
22 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
23
24 for(i = 0; i < THREAD_CNT; i++) {
25 pthread_create(&tid[i], &attr, func, (void *)i);
26 //pthread_join(tid[i], NULL);
27 sleep(1);
28 }
29
30 sleep(1);
31
32 return 0;
33 }
这三种改动方式运行的程序占用的资源大小都不会变大,表明在线程结束之后资源确实回收了。
结论:
为防止线程退出时没有释放资源造成资源浪费,可以用三种方式防止资源浪费:
1、在线程函数中调用pthread_detach(pthread_self()); 让自己变为分离线程;
2、在创建线程的时候传入分离属性,调用pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 让属性变为分离属性。PTHREAD_CREATE_DETACHED的值其实就是1,PTHREAD_CREATE_JOINABLE的值为0(需包含pthread.h)。
3、使用pthread_join回收资源。
需要注意的是pthread_join会阻塞等待线程结束,而如果传入的线程是分离属性,那么pthread_join函数会立即返回EINVAL(值为22,需包含errno.h)。
另外,对于https://blog.csdn.net/qiurisuixiang/article/details/6648213这篇博客中提到的这个问题:
我进行了验证,使用的工具为valgrind。确实如果通过给线程设置分离属性的方法去回收资源,确实有可能会报“可能的泄漏”,而如果使用pthread_join则不会。但就我个人而言并不喜欢使用pthread_join来回收内存,原因是pthread_join会造成阻塞,这就需要考虑pthread_join调用的时机。并且我已经用ps -aux证实了给线程设置分离属性不会造成泄漏,因此valgrind它报可能泄漏就让它报去吧。