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多线程编程,用的多的话,其实需要学习的地方还有很多,感觉这些能够满足一些基本的使用了,其他的比如信号量等,等再有使用上的心得的时候再上来总结了。