概念
POSIX线程的一个特点是:除非线程是被分离的了,否则在线程退出的时候,它的资源是不会被释放的。所以POSIX提供了pthread_detach函数用来分离线程:
int pthread_detach(pthread_t thread);
它设置线程的内部选项来说明线程退出后,其所占有的资源可以被回收。参数thread是要分离的线程的ID。
pthread_join函数可以使调用这个函数的线程等待指定的线程运行完成再继续执行。它的形式为:
int pthread_join(pthread_t thread, void ** value_ptr);
参数thread为要等待的线程的ID,参数value_ptr为指向返回值的指针提供一个位置,这参数thread为要等待的线程的ID,参数value_ptr为指向返回值的指针提供一个位置,这个返回值是由目标线程传递给pthread_exit或return的。
实例
例1
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
void * thread1(void *arg)
{
int i;
for (i=0 ; i < 5; i++)
{
usleep(500*1000);
printf("thread1 running!\n");
}
printf("leave thread1!\n");
}
int main(int argc,char** argv)
{
pthread_t tid;
pthread_create(&tid, NULL, (void*)thread1, NULL);
pthread_join(tid,NULL);
printf("Leave main thread!\n");
return 1;
}
运行结果:
例2
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
void * thread1(void *arg)
{
int i;
for (i=0 ; i < 5; i++)
{
usleep(500*1000);
printf("thread1 running!\n");
}
printf("leave thread1!\n");
}
int main(int argc,char** argv)
{
pthread_t tid;
pthread_create(&tid, NULL, (void*)thread1, NULL);
//pthread_join(tid,NULL);
printf("Leave main thread!\n");
return 0;
}
运行结果:
结果分析:
通过上面1和2两个例子可以发现在主进程中调用了pthread_join后,就必须等待子进程thread1运行结束以后才能接着往下执行。
例3
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
void * thread(void *arg)
{
int i = *((int*)arg);
usleep(500*1000);
printf("thread%d running!\n",i);
return NULL;
}
int main(int argc,char** argv)
{
pthread_t tid;
int i = 0;
while(1)
{
pthread_create(&tid, NULL, (void*)thread, &i);
//pthread_join(tid,NULL);
pthread_detach(tid);
usleep(1000*1000);
i++;
}
printf("Leave main thread!\n");
return 0;
}
运行结果:
在当前命令窗口下会一直打印线程运行
然后我们用Xshell远程登录虚拟机,输入ps -ef
查看运行程序的进程号,如图所示:
再输入top -d 1 -p XXXX(我这边是20302)
,可以看到在VIRT虚拟内存这一栏的数据一直在增大。
例4
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
void * thread(void *arg)
{
int i = *((int*)arg);
usleep(500*1000);
printf("thread%d running!\n",i);
return NULL;
}
int main(int argc,char** argv)
{
pthread_t tid;
int i = 0;
while(1)
{
pthread_create(&tid, NULL, (void*)thread, &i);
//pthread_join(tid,NULL);
pthread_detach(tid);
usleep(1000*1000);
i++;
}
printf("Leave main thread!\n");
return 0;
}
运行结果:
结果分析:
在第3个例子中当前进程的VIRT(虚拟内存)会一直增大,而第4个例子中的VIRT保持在80424不变,这是因为在第3个例子中,创建一个完子线程以后,没有用 pthread_detach(tid) 把他设置为分离的,所以在子线程执行完以后,资源没有释放掉,等待主线程去读取状态,最后就造成了虚拟内存越来越大。
例5
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
void * thread(void *arg)
{
int i = *((int*)arg);
usleep(500*1000);
printf("thread%d running!\n",i);
return NULL;
}
int main(int argc,char** argv)
{
pthread_t tid;
int i = 0;
while(1)
{
pthread_create(&tid, NULL, (void*)thread, &i);
pthread_join(tid,NULL);
//pthread_detach(tid);
usleep(1000*1000);
i++;
}
printf("Leave main thread!\n");
return 0;
}
运行结果:
结果分析:
在第5个例子中当前进程的VIRT(虚拟内存)还是不变,是因为主线程创建完一个子线程以后用pthread_join(tid,NULL); 进入阻塞状态,等待子线程运行结束后释放资源。而子线程运行到最后,因为有线程去读取他的状态,所以就不会出现例4中一直占用资源而不释放的情况。所以最后例5的结果和例4一样虚拟内存大小不变
总结
所以在我们的日常使用中,为了避免子线程运行结束但是占用资源不释放,可以使用pthread_detach与pthread_join两个函数。