多线程中的资源释放

代码中的问题

最近在写多线程的代码,这段程序虽然不要求像服务器软件一样不间断的长期运行,但是运行的时间也可能达到数天。这段代码的功能大概是一个线程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。


解决:软件多线程运行时遇到【内存不断升高】甚至爆表! 因为本人是个小白,多线程经常用,但是线程池并没有用过,(一听到线程池,总感觉高大上) 但是近期写彩票软件的时候发现,多线程长期操作会导致内容不断的升高直至报错,遂想起了线程池,完善后发现不是一般的叼 啊!!! 先简单的说下多线程和线程池的区别: 1、多线程每次启动的时候系统都要分配一定的资源出来(主要占的就是内存),而不断的启动线程、启动线程、启动线程 循环的启动线程,就造成了系统资源极大的浪费,甚至不释放的情况,继续下去内存就OVER了! 2、线程池则完美的解决了这个问题,线程池的原理就是事先申请好指定数量的线程所使用的资源,而且这些资源是不断的重复利用的!可利用任务管理器看到程序的线程数量的变化(在使用普通的多线程时:线程数会根据软件启动的线程数量增加,循环完了之后线程数量也就减少了,但是内存资源不减少,再启动线程内存继续飙升!  而在使用【线程池】的时候:线程数一直保持线程池的数量,无论你是否启动多线程进行运算,线程数量都不会变化,同时内存也不会有多大的变化,更不会不断的飙升!) 也许我的表达能力不足,反正大家知道在启用多线程的时候尽量使用线程池可以保证内存不会飙升就行了! 这里说的启动多线程是指循环启动‘同一个子程序’线程:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值