pthread库学习

来源:互联网 发布:广东移动网络测速平台 编辑:程序博客网 时间:2024/06/05 20:45
1.pthread_create 创建线程发生内存分配不足错误,通常是线程数过多。自已机子ubuntu8.04能成功创建线程大小为382。

为了能创建更多线程,第一条路是创建多进程,第二条路是减少线程栈的大小。

通常线程栈的大小受制于两个因素:
其一栈的最小值,通过宏PTHREAD_STACK_MIN可能得到大小,我的机子为16384即16k,任何线程不能小于此值,而且这个值没有考虑线程的具体逻辑,仅仅启动线程所需的大小。通常线程还要局部变量,线程大小应该比它大。
其二系统限制栈最大值,可以通过ulimit -s系统命令,得到本系统的最大值,我的机子为8192即8M,同时任何线程不能超过此值(可以利用ulimit -s xxx,修改成xxx值,单位为k)。
利用pthread_create创建线程时,pthread库会自动地为线程设定栈大小,我们通常不用明显地指定。当发生了内存分配不足时,就必须我们自已动手来指定栈大小。
pthread库用pthread_attr_t类型变量,来操纵线程属性。我们可以利用pthread_attr_getstacksize来得到默认分配大小,注意到必须先调用pthread_attr_init初始化函数,才能得到正确的值,否则
得到的值是不正确的。
示例代码:
pthread_attr_t attr;
pthread_attr_init(&attr);
std::size_t stack_size;
pthread_attr_getstacksize(&attr, &stack_size);

我的机子上是8388608即8192K即8M。

为什么系统有一个限制栈最大值呢?因为现在我们的cpu位数有限,例如我的32位,则cpu的寻址空间为4G,因此每一个进程的空间为4G。因为进程需要操作系统的支持,需要划分一部分空间给进入操作系统内核时候
使用,称之为内核态。例如需要系统调用,需要内核栈存放数据,需要同步通信数据等等。在不同的操作系统中划分的空间是不太相同的,linux下一般为1G,windows系统下一般2G。
进程去除内核态占用的空间外,就是用户态的空间了。
用户态空间划分成代码段,数据段,栈段。其中在linux中代码段分配在低地址,只读的。接下来是数据段,数据段细划分为全局(已初始化)静态(已初始化)数据
区,常量区,全局(未初始化)静态(未初始化)区。接下来就是栈空间了,但是栈空间访问方式与其它段不相同,栈分配空间是从高到低(通过esp先指向高地址,然后减去相应大小数据,达到分配空间的目的)。
因为一般情况下,数据段最后一个地址与栈的esp指向的地址存在相大的空间,这些空间就可以让我们做一些事情。可以利用这些空间动态地分配,称之为堆,可以利用这些空间分配给线程,称之为线程栈。
如何得到这段空间的起始地址呢?在linux下存在一个系统调用system_brk,给此函数传0作为参数,其返回值就是起始地址。
通常进程的用户态空间中我们可以自由控制的只有这段空间。linux下这段空间的大小一般为3000M左右(当然如果程序代码量较大,会更少),而用户态空间总共3G(3072M)大小。
因为创建线程栈中也只有在这段空间,所以系统要在数量和大小做一个平衡,因此存在一个栈最大值。

如何确定线程栈的大小呢?主要看线程函数中用的局部变量多少,动态申请空间的不算(但也会间接影响到线程数量的),特别要引起注意的是线程函数中代码不算,不要看线程函数代码非常多,就以为线程函数代码会占很多栈空间,前面说过,代码是放在代码段的,同理会间接影响到线程数量。因为总空间即用户空间有限,当代码段增大,数据段增大,自然自由空间就会减少。如果程序代码中使用了堆的话,还要去掉堆的空间。
所以在栈最小值基础上加上相应局部变量空间,再适当地增加一些空间(为安全起见)就可以了。

以前是个人参考各种资料理解总结而成的,并不代表一定是对的,欢迎有朋友指正。

2.利用pthread_cond_t类型变量实现条件通信,关键在于理解pthread_cond_wait函数,也是灵活运用条件通信的关键。
pthread_cond_wait需要二个参数,一个是条件变量地址,一个是mutex型变量地址。第一个参数很好理解,因为需要等待条件变量变化,所以需要它地址。
第二个参数却不易理解,明明我使用条件变量通信与mutex有什么关系,为什么需要它呢?看起来是多此一举,把问题复杂化了。
呵呵,我自已用sem操作来模拟没有mutex的条件变量,结果很惨,具体代码不好意思拿出来了,主要问题出在没办法确保sem的顺序。
假设a线程专门通过sem_post来实现点灯,通知资源可用。
假设b线程专门通过sem_wait来实现等待灯亮,并且消费资源和灭灯。
a,b两个线程,独立运行,推进速度未知,看起来,好像实现同步。假设b线程足够快,a线程慢一点,应该没多大问题。如果b线程非常慢,a线程非常快的话,有可能会产生两个问题:
1。资源未被消费,却被新资源刷新,重复点灯。
2。读“脏数据”,即当b线程消费了一部分资源,被切换成a线程,a线程用新资源刷新,当再切换回b线程时,接下来消费资源就是新资源,与上次消费资源不同步,如果b线程消费资源
与整个资源相关的,就会产生错误的结果。

所以类似这种问题,需要一个mutex来保证当正在消费资源或者产生新资源的独占性。
所以也就知道为什么条件变量通信需要mutex支持。
再来看pthread_cond_wait具体逻辑,它先释放mutex锁,然后等待信号,如果有信号通知,它重新获得mutex锁,并返回。
因为pthread_cond_wait先要释放锁,所以在它调用之前,一定要先获得锁,否则谈不上释放锁。
至于pthread_cond_wait返回后,要不要释放锁,则看具体的逻辑代码需要,一般情况下是需要的。

需要引起注意的是,当pthread_cond_wait返回的时候,并一定代表所求条件成立,强烈建议重新检查。
综上所述,使用pthread_cond_wait的示例代码为:
pthread_mutex_lock(&mutex);
while (!is_my_need)
{
pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
与pthread_cond_wait配合使用的函数为pthread_cond_signal, pthread_cond_broadcast函数。

3.pthread_create创建线程出现“Resource temporarily unavailable”错误时,表示线程创建过多,至于如何修改,我暂时还没有找到解决的办法。
有人说要用pthread_detach分离,但是我试过了,我的测试程序没通过。
原创粉丝点击