linux使用pthread库多线程编程,即使设置pthread_detach,资源也残留部分,不完全释放问题

下面这段代码,是利用 pthread 库进行多线程编程的最简单的 demo 了:

#include "stdio.h"
#include "pthread.h"
//#include ...

void* testOcupMem(void* p)
{
    // pthread_detach(pthread_self());
    printf("                    %d testOcupMem exit\n", gettid());
    return NULL;
}

int main()
{
    int i = 15;
    pthread_t pid;

    while(i--){
        printf("---------------- %d\n", i);
        if(0>pthread_create(&pid, NULL, testOcupMem, &pid)){
           printf("---- create thread readVideo failed\n");
        }
        sleep(2);   
    }
    printf("\n------------- getchar --------------\n");
    getchar();
    return 0;
}

在 main 函数中,共创建 15 次线程,线程函数打印“testOcupMem exit”就退出。按理说,线程函数退出后,资源就被释放了,但是编译后执行,发现每次创建线程,内存占用都会增加。


于是 man 一下 pthread 函数,发现这么描述:

里面说到,线程分为 joinabledetached。只有两种情况,资源才会在线程结束时释放:

  • 如果线程是 joinable,则需要调用 pthread_join
  • 如果线程是 detached,则资源会随着线程函数结束,自动释放

这就视情况而定了,因为如果主线程使用 pthread_join,主线程会阻塞,难以做到和子线程共同工作。所以,这里将线程函数设置为 detached。方法很简单,就是调用 pthread_detach 函数。将上面 demo 线程函数中的 pthread_detach 函数解注释,再编译执行,发现程序使用内存不再随着创建线程次数增加了。

但是

我们在主线程创建子线程之前,加入 getchar() 使主线程暂停。

...
int main()
{
    int i = 15;
    pthread_t pid;

    getchar();

    while(i--){
        ...

再编译执行,发现在创建子线程之前,程序占用内存仅为 1288

接着,按下回车,主线程开始创建子线程,发现创建子线程后,内存增加到 9484

**虽然程序多次创建子线程,使用内存不再继续增加,但是子线程全部退出后,程序仍然占用内存仍然为 9484**,这部分资源并没有释放,多出了 8196。

继续 man phread_detach函数:

提到,线程资源在程序退出的时候才全部释放。

查看系统设置的堆栈大小:

# ulimit -s
8192

因此猜测:主线程创建子线程时使用的堆栈没有释放,仍然残留在主线程里。于是查阅 pthread_create 原理,发现它调用了 clone 函数。下面的 demo 模拟了 pthread_create 函数:

#define _GNU_SOURCE
#include "stdio.h"
#include "pthread.h"
#include "sched.h"
void* testOcupMem(void* p)
{
    printf("                    %d testOcupMem exit\n", gettid());
    return NULL;
}
#define STACK_SIZE 8192*1024
int main()
{
    int i = 15;

    void* pstk = malloc(STACK_SIZE);
    if(NULL == pstk){
         printf("query failed, cannot malloc stack\n");
         return -1;
    }
    while(i--){
        printf("---------------- %d\n", i);
        int clonePid = clone((int(*)(void*))testOcupMem,  (char *)pstk + STACK_SIZE,
                     CLONE_VM | CLONE_FS  | CLONE_FILES | SIGCHLD, pstk);
        printf("  clonePid: %d\n", clonePid);
        if(-1 ==  clonePid){    
            printf("query failed, cannot start query process\n");
            free(pstk);
            return -1;
        }
        sleep(2);   
    }
    free(pstk);         // pthread_create 创建线程时,堆栈没有释放
    printf("\n------------- getchar --------------\n");
    getchar();
    return 0;
}

模拟的最后部分,将堆栈释放掉,发现被一直子线程被创建前后,多占用的 8000 多内存空间被释放了。

所以,如果很在意这部分堆栈空间,可以用 clone 自己封装一个简易版的 pthread_create 函数。

阅读更多:   C语言 , Linux笔记
添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif