您的位置:首页 > 其它

多线程中的资源释放

2013-11-08 16:19 1036 查看

代码中的问题

最近在写多线程的代码,这段程序虽然不要求像服务器软件一样不间断的长期运行,但是运行的时间也可能达到数天。这段代码的功能大概是一个线程A负责分配一块内存,然后填充数据,交给另一个线程B进行处理,线程B处理之后将内存释放。功能很简单,但是问题出现了,测试的时候发现内存持续增长,没有下降的迹象(就好象内存从来没有被释放一样),使用valgrind这样的工具检查居然没有发现内存泄漏,真是奇怪呀!

后来找到了原因,问题在于线程A在创建线程B的时候使用了joinable线程,并在A结束之前调用pthread_join来等待所有的B结束。B是joinable类型的线程,这样的线程中释放的内存是不会马上被系统回收的,要等到pthread_join之后才会被回收。这样上面的现象就好解释了,每一个B在运行结束之后它释放的内存并没有被系统回收,这样随着A不断的分配内存并产生新的B,系统占用的内存越来越多。但是在整个程序结束的时候A使用了pthread_join来连接了每个B,所以在A结束的时候所有被释放的内存才一起被系统回收,所以实际上并没有内存泄漏,这样valgrind也就没有检查出内存泄漏。

线程属性

man page中的描述

先看看linux关于pthread_create的描述:

A thread may either be joinable or detached.  If a thread is joinable, then another thread can
call pthread_join(3) to wait for the thread to terminate  and  fetch its  exit status.  Only
when a terminated joinable thread has been joined are the last of its resources released back
to the system. When a detached thread terminates, its resources are automatically released
back to the system: it is not possible to join with the thread in order to obtain its exit
status.  Making a thread detached  is useful for some types of daemon threads whose exit
status the application does not need to care about.  By default, a new thread is created in
a joinable state, unless attr was set to create the thread in a detached state (using
pthread_attr_setdetachstate(3)).

从手册的描述中可以看出线程分为joinable和detached两种,并且两种线程对于资源的释放方式是不同的,我们上面的问题也就是由于用错了线程类型造成的。

joinable类型的线程

从man手册中可以看出,在不使用属性指定detached的情况下使用pthread_create创建线程就是joinable线程,它需要pthread_join来连接:

pthread_create(&tid, NULL, thread_func, NULL);

这样就创建了一个joinable线程。

detached类型的线程

man手册中说创建detached类型的线程需要线程属性,并且调用pthread_attr_setdetachstate函数来设置detached属性:

pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, thread_func, NULL);

这样就创建了一个detached类型的线程,这样的线程不需要使用pthread_join来等待其结束,也不能够使用pthread_join来等待其结束。

除了在创建的时候指定detached属性来创建detached类型的线程之外,还可以使用pthread_detach函数将一个joinable类型的线程分离,使它变成detached类型的线程。有两种方式可以达到这种目的。现在假设线程A创建了一个joinable类型的线程B,要使用pthread_detach函数将B分离。

第一种方式是A创建B之后调用pthread_detach:

pthread_create(&tid, NULL, thread_func, NULL);
pthread_detach(tid);

这样线程B(也就是线程函数thread_func)变成了detached类型的线程。

第二种方式是B被创建之后自己调用pthread_detach来分离自己:

线程A的代码:

pthread_create(&tid, NULL, thread_func, NULL);


线程B的代码:

void* thread_func(void* args)
{
pthread_detach(pthread_self());
...
}

这样线程B就分离了自己。

这两种方式不能够同时使用,因为pthread_detach函数分离的对象只能是joinable类型的线程,否则会失败。

pthread_join和pthread_detach的含义

joinable和detached两种类型的函数也对应着pthread_join和pthread_detach两个函数,这两个函数其实是与线程的资源释放类型相关的,由这两个函数来决定资源释放的时机。线程被创建之后必须调用其中一种来释放资源。detached类型的线程结束之后系统自动的回收了资源,但是joinable类型的线程如果创建它的线程没有调用pthread_join那么这就是真正的资源泄漏。

pthread_exit还是return?

关于多线程代码的资源释放还有一个有趣的问题,进程中的主线程提前结束并退出,这时候其他的子线程还没有完成自己的工作,这个时候会发生什么呢?其他的子线程立即退出,整个进程退出还是进程等到每一个子线程结束之后再退出呢?

这要看看pthread_exit的man手册:

Performing a return from the start function of any thread other than the main thread results in
an implicit call to pthread_exit(),  using  the  function’s  return value as the thread’s
exit status.

To allow other threads to continue execution, the main thread should terminate by calling
pthread_exit() rather than exit(3).

The value pointed to by retval should not be located on the calling thread’s stack, since the
contents of that stack are undefined after the thread terminates.


这样就清楚了,在子线程中pthread_exit和return是一样的,但是在main函数中不行,return会被替换成exit,这导致了进程直接终止,那么此时没有结束的子线程就会被迫停止,所以要让main退出时子线程继续运行就必须在main中使用pthread_exit来代替return。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: