pthread多线程学习笔记六线程属性篇
其实线程属性我看明白的很少,因为属性很多,很多都永不到,很多虽然看了却没法试验下,因此不知道对错。感觉关于多线程有句话说的很对,有很多多线程的程序,只有在多个机器,多个系统上都试验过,才知道对不对,错在哪里。
在/usr/include/pthread.h里可以看到一堆关于set or get attr的函数,大概全是这个样子的:pthread_attr_set*() 或者pthread_attr_get*();
想了下,先从一个普通例子开始说起:
#include <stdio.h>
#include <unistd.h>
int main()
{
const size_t asize = (8192*1024+1024*1024*2)/sizeof(int);
/*const size_t asize = 8388608/sizeof(int);*/
printf("heap:\n");
int *parray;
parray = malloc(asize*sizeof(int));
parray[asize-1] = 1;
printf("stack:\n");
int array[asize];
printf("%u\n",asize);
array[asize-1]=1;
return 0;
}
程序输出:
heap:
stack:
段错误
为什么会这样?因为每个进程的栈大小是有限制的,例如我的:
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 20
file size (blocks, -f) unlimited
pending signals (-i) 16382
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
通过ulimit –s可以修改进程栈的大小使这个程序运行OK(也可能不行,跟硬件有关)。
我们这里数组大小不止用到了8192k,因为linux下还有一个栈的缓冲区。
实际上进程在运行时并不一定会用到8192k大小的栈,这是一个最大值,可以写两个使用不同栈大小的例子,pause后,查看/proc/*/maps试验下。
但线程是不同的,线程的栈可以通过暴露栈指针的方式共享。无论线程使用多小的栈,进程都会分配一定数目(我的是8M,可以通过线程属性函数来查看)大小的栈给线程使用。如果线程不是以detach结束的,那么只有用join函数获得线程退出状态后才能回收该线程资源,这就是最开始我们说到join函数的作用。
用一个例子测试下线程数目:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void *test(void *arg)
{
/*pthread_detach(pthread_self());*/
}
int main()
{
int err;
int i = 0;
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr,1024*1024*16);
while (1) {
err = pthread_create(&tid,NULL,test,NULL);
/*err = pthread_create(&tid,&attr,test,NULL);*/
if (err!=0) {
printf("create thread error: %s!\n",strerror(err));
exit(1);
}
++i;
printf("i=%d\n",i);
}
return 0;
}
我的机器上结果是381,有时382,注意此时这里返回值就是最开始提到的EAGAIN。如果注释放开的话,就可以一直运行下去,因为资源被回收。不过用有的机器试验时,数量虽然增大了,但并不能一直运行下去。没想清楚什么原因。
程序里有几个关于属性的函数,不过暂时没有用到,关于函数的作用,在pthread.h里可以直接看到。
从网上搜到的关于线程最大数目的解释:
这个值和理论完全相符,因为 32 位 linux 下的进程用户空间是 3G 的大小,也就是 3072M,用 3072M 除以 8M 得 384,但是实际上代码段和数据段等还要占用一些空间,这个值应该向下取整到 383,再减去主线程,得到 382。
那为什么 linuxthreads 上还要少一个线程呢?这可太对了,因为 linuxthreads 还需要一个管理线程
为了突破内存的限制,可以有两种方法
1) 用 ulimit -s 1024 减小默认的栈大小
2) 调用 pthread_create 的时候用 pthread_attr_getstacksize 设置一个较小的栈大小
要注意的是,即使这样的也无法突破 1024 个线程的硬限制,除非重新编译 C 库。
虽然结果不是确定的,不过这个解释感觉也还是很合理的。
可以通过程序里线程的attr(即在create的时候不采用NULL而采用这个attr)建立线程观察下线程最大数目再计算下验证下结果。
关于最开始那个程序的段错误,在线程里一样会有,相应的通过pthread_attr_setstacksize(&attr,stackSize)就可以解决了。
关于属性,很多一知半解的,就不拿上来贻笑大方了。
关于pthread多线程编程,用的多的话,其实需要学习的地方还有很多,感觉这些能够满足一些基本的使用了,其他的比如信号量等,等再有使用上的心得的时候再上来总结了。