线程创建时,系统会分配给线程一些资源,我们可以看到的就是线程描述符,线程堆栈,在系统内部还会有更复杂的系统维护一些信息,在线程创建时,内核总会为其维护一些资源,比较理想的情况是线程运行结束后,释放系统资源和进程资源,包含线程返回值占用的内存,线程堆栈,寄存器状态等等,以备后来者的使用.
线程执行结束后释放资源的三种方法:
利用这些方法,我们可以避免线程退出时,系统资源仍然无法释放的情况:
- pthread_join,这个函数的原型是pthread_join(pthread_t thread, void **value_ptr),作用是使调用函数的线程阻塞等待指定线程退出。只有线程是可接入的时候,调用pthread_join才会成功。
- pthread_detach,这个函数可以改变线程的可接入属性,将其改变成分离模式。当线程处于分离模式,表明我们对于线程的返回值并不关心,工作线程执行完毕后会自行退出,内核会释放该线程占用的资源。
- 设置线程属性结构pthread_attr_t为可分离的,如pthread_attr_init(&attr);pthread_attr_setdetach(&attr,PTHREAD_CREATE_DETACHED),并用此线程属性对象参与pthread_create函数中,这样创建出的线程都是分离的。
识别泄漏
如果您创建一个可接合的线程,但是忘记联接它,其资源或私有内存一直保存在进程空间中,从未进行回收再利用。一定要联接可接 合的线程;否则,可能会引起严重的内存泄漏问题。
例如,Red Hat Enterprise Linux (RHEL4)上的一个线程需要一个 10MB 的堆栈,这意味着,如果不联接它,会有至少 10MB 的内存泄漏。假设您设计一个管理器-工作线程模式的程序来处理传入的请求。然后需要创建越来越多的工作线程来执行各个任务,最后终止这些线程。如果它们是 可接合的线程,且您没有调用 pthread_join()
来联接它们,那么在线程终止后,每个产生的线程都将泄漏大量的内存(至少每堆栈 10MB)。随着创建并在未联接的情况下终止的工作线程越来越多,泄漏的内存量也持续增加。另外,进程将无法创建新的线程,因为无内存可供创建新线程使 用。
清单 1 显示在忘记联接可接合线程时引发的严重内存泄漏。您还可以使用该代码检查可在一个进程空间中共存的线程体的最大量。
#include<stdio.h>
#include<pthread.h>
void run() {
pthread_exit(0);
}
int main () {
pthread_t thread;
int rc;
long count = 0;
while(1) {
if(rc = pthread_create(&thread, 0, run, 0) ) {
printf("ERROR, rc is %d, so far %ld threads created\n", rc, count);
perror("Fail:");
return -1;
}
count++;
}
return 0;
}
清单 1 中调用了 pthread_create()
来创建一个含默认线程属性的新线程。默认情况下,新创建的线程是可接合的。它不断创建新的可接合线程,直至有故障发生。然后输出错误代码和故障原因。
使用以下命令在 Red Hat Enterprise Linux Server 5.4 上编译清单 1 中的代码时: [root@server ~]# cc -lpthread thread.c -o thread
, 您将获得清单 2 所示的结果。
清单 2. 内存泄漏结果
|
在代码创建了 304 个线程之后,它无法创建更多线程。错误代码是 12
,这表示无更多内存可用。
如清单 1 和清单 2 所示,虽然生成了可接合线程,但是却未将其联接,因此每个终止的可接合线程仍然占用进程空间,泄漏进程内存。
RHEL 上的一个 POSIX 线程拥有一个大小为 10MB 的私有堆栈。换言之,系统为每个 pthread 分配至少 10MB 的专用存储。在我们的示例中,304 个线程是在进程停止前创建的;这些线程占用 304*10MB 内存,合计约 3GB。一个进程的虚拟内存的大小是 4GB,其中四分之一的进程空间是为 Linux 内核预留的。这样一来,就有 3GB 的内存空间可用作用户空间。因此,3GB 内存由死线程消耗。这是很严重的内存泄漏。而且很容易理解它发生的速度为何如此之快。
要修复泄漏,您可以添加代码调用 pthread_join()
,该方法可联接每个可接合线程。
1)、主线程等待子线程结束
可以用pthread_join函数,pthread_join()函数会等待指定线程的结束,也就是主线程会为子线程阻塞,如果子线程没有执行完,那么主线程就永远不会执行phread_join()下面的程序。程序如下:
#include <stdio.h>
#include <pthread.h>
void pthread_1()
{
printf("I am the child!!\n");
sleep(2);
printf("I am exit!!\n");
}
void main()
{
pthread_t tid;
void *tret;
pthread_create(&tid, NULL, &pthread_1, NULL);
pthread_join(tid, &tret);
printf("Thread exit with code %d\n", (int)tret);
exit(0);
}
2)、主线程终止子线程
在posix thread中线程有两中取消状态:立即取消和延迟取消
立即取消就是pthread_cancel之后,不管理线程在干什么,马上终止这个线程
而延迟取消是在pthread_cancel之后,线程会继续运行,直到遇到一个 "取消点函数 "
系统默认的是延迟取消
如果想要结束线程有几个方法
a.线程设置为立即取消
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
程序如下
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
void pthead_1()
{
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
while(1)
{
printf("I am the child \n");
usleep(10000);
}
}
void main()
{
pthread_t pthread_id;
pthread_create(&pthread_id, NULL, &pthead_1, NULL);
sleep(2);
pthread_cancel(pthread_id);
printf("I make the child exit!!\n");
pthread_exit(0);
}
b.在你的线程中加入一些取消点函数的调用
while( 1 )
{
//sleep(1)或者pthread_testcancle();
}