下面这段代码,是利用 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 函数,发现这么描述:
里面说到,线程分为 joinable
和 detached
。只有两种情况,资源才会在线程结束时释放:
- 如果线程是 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 函数。