Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1736476
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: 系统运维

2012-03-31 23:01:32

在十一章里我们调用过pthread_create的所有例子里,我们传递一个空指针而不是一个pthread_attr_t结构体的指针。我们可 以使用pthread_attr_t结构体来修改默认行为,并把这些属性和我们创建的线程关联起来。我们使用pthread_attr_init函数来初 始化pthread_attr_t结构体。在调用pthread_attr_init后,pthread_attr_t结构体包含了实现支持的所有线程属 性的默认值。要改变单个属性,我们需要调用其它函数,本节后面就描述。



  1. #include <pthread.h>

  2. int pthread_attr_init(pthread_attr_t *attr);

  3. int pthread_attr_destroy(pthread_attr_t *attr);

  4. 成功返回0,失败返回错误号。


为 了反初始化一个pthread_attr_t结构体,我们调用pthread_attr_destroy。如果pthread_attr_init的一个 实现为属性对象分配了任何动态内存,那么pthread_attr_destroy会释放那个内存。此外,pthread_attr_destroy将会 用无效值初始化属性对象,所以如果它被误用,pthread_create会返回一个错误。


pthread_attr_t结构体对应用透明。这意味着应用不被支持知道任何它内部的结构,因此促进应用的移植性。根据这个模型,POSIX.1定义了独立的函数来查询和设置每个属性。


POSIX.1定义的线程属性在下表中汇总。POSIX.1定义了实现线程选项里的补充属性,但是我们不在这里讨论。在下表,我们也展示了哪些平台支持各个线程属性。如果属性通过废弃的接口可访问,我们在表项里显示ob。


POSIX.1线程属性
名字描述FreeBSD 5.2.1Linux 2.4.22Mac OS X 10.3Solaris 9
detachstate分离的线程属性****
guardsize在线程栈末尾的保卫缓冲的字节尺寸
***
stackaddr线程栈的最低地址ob**ob
stacksize线程栈的字节尺寸****


在11.5节,我们介绍了分离线程的概念。如果我们不再对已有线程的终止状态感兴趣,那么我们可以使用pthread_detach来允许操作系统在线程退出时回收这个线程的资源。


如 果我们在创建这个线程的时候知道我们不需要线程的终止状态,那么我们可以安排线程以分离状态启动,通过修改pthread_attr_t结构体里的 detachstate属性。我们可以使用pthread_attr_setdetachstate函数来设置detachstate线程属性为两个合法 值中的某一个:PTHREAD_CREATE_DETACHED来以分离状态启动线程,或PTHREAD_CREATE_JOINABLE来正常启动线 程。所以它的终止状态可以被应用得到。



  1. #include <pthread.h>

  2. int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *detachstate);

  3. int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

  4. 成功返回0,失败返回错误号。


我 们可以调用pthread_attr_getdetachstate来得到当月的detachstate属性。被第二个参数指向的整型被设置为 PTHREAD_CREATE_DETACHED或PTHREAD_CREATE_JOINABLE,取决于给定的pthread_attr_t结构体的 这个属性的值。


下面的代码展示了一个可以用来以分离状态创建一个线程的函数:



  1. #include <pthread.h>

  2. int
  3. makethread(void *(*fn)(void *), void *arg)
  4. {
  5.     int err;
  6.     pthread_t tid;
  7.     pthread_attr_t attr;

  8.     err = pthread_attr_init(&attr);
  9.     if (err != 0)
  10.         return(err);
  11.     err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  12.     if (err == 0)
  13.         err = pthread_create(&tid, &attr, fn, arg);
  14.     pthread_attr_destroy(&attr);
  15.     return(err);
  16. }

注意我们忽略pthread_attr_destroy的返回值。在这种情况下,我们恰当地初始化了线程属性,所以 pthread_attr_destroy不应该失败。尽管如此,如果它确实失败了,那么清理将是困难的:我们必须销毁我们刚创建的线程,它可以已经正在 运行了,异步于这个函数的执行。通过忽略这个从pthread_attr_destroy返回的错误,最坏可以发生的是我们泄漏了任何 pthread_attr_init分配的少量内存。但是如果pthread_attr_init成功初始化了线程属性而然后 pthread_attr_destroy又清理失败了,那么我们也没有任何恢复的策略,因为属性结构体对于应用是透明的。为清理这个结构体而定义的唯一 接口是pthread_attr_destroy,而它失败了。

对于线程栈属性的支持对于POSIX操作系统是可选的,但是如果系统 遵守XSI则是必需的。在编译器,你可以检查你的系统是否支持每个线程栈属性,使用_POSIX_THREAD_ATTR_STACKADDR和 _POSIX_THREAD_ATTR_STACKSIZE符号。如果某个被定义了,那么系统支持对应的线程栈属性。你也可以在运行时检查,通过使用 _SC_THREAD_ATTR_STACKADDR和_SC_THREAD_ATTR_STACKSIZE参数给sysconf函数。


POSIX.1 定义了几个操作线程栈属性的接口。两个更老的函数,pthread_attr_getstackaddr和 pthread_attr_setstackaddr,在SUS的版本3里被标记为废弃的,尽管许多pthreads实现仍提供它们。查询和修改线程栈属 性的最好方法是使用更新的函数pthread_attr_getstack和pthread_attr_setstack。这些函数去除了更老的接口定义 里表现中的歧义。



  1. #include <pthread.h>

  2. int pthread_attr_getstack(const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restric stacksize);

  3. int pthread_attr_setstack(pthread_attr_t *attr, void *statckaddr, size_t *stacksize);

  4. 两者成功返回0,失败返回错误号。


这两个函数被用来同时管理stackaddr和stacksize线程属性。


对 于一个进程,虚拟地址空间量是固定的。因为只有一个栈,它的尺寸通常不是问题。对于线程,同一个虚拟地址空间的量必须被所有线程栈共享。你可能必须减少你 默认的线程栈尺寸,如果你的应用使用很多线程,以至栈的总尺寸超过可用的虚拟地址空间。另一方面,如果你的线程调用分配巨大自动变量的函数或调用很深的栈 框架的函数,那么你要比需要比默认栈尺寸更多。


如果你为线程栈用完了虚拟进程空间,那么你调用malloc或mmap(14.9节)来为一 个代替的栈分配空间,并使用pthread_attr_setstack来改变你创建的线程的栈位置。stackaddr指定的地址是用于线程栈的最低可 寻址地址,根据处理器架构对齐到合适的边界。


stackaddr线程属性被定义为栈的最低内存地址。然而,这不必是栈的开始。如果对于给定的处理器架构栈从高位地址向低位地址增长,那么stackaddr线程属性将是栈的末尾而不是开头。


pthread_attr_getstackaddr 和pthread_attr_setstackaddr的缺陷是stackaddr参数是未详细说明的。它可能已经被解释为栈的开头或作为栈使用的内存扩 展的最低内存地址。在栈从高位内存地址向低位地址住下增长的架构上,如果stackaddr参数是栈的最低内存地址,那么你需要知道栈的尺寸来决定栈的开 头。

pthread_attr_getstack和pthread_attr_setstack函数更正了这个缺点。


一个应用也可以用pthread_attr_getstacksize和pthread_attr_setstacksize函数来得到和设置stacksize线程属性。



  1. #include <pthread.h>

  2. int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize);

  3. int pthread_attr_setstackszie(pthread_attr_t *attr, size_t stacksize);

  4. 两者成功返回0,失败返回错误号。


当你想改变默认栈大小而不想自己处理分配线程栈时,pthread_attr_setstacksize函数很有用。


guardsize 线程属性控制了线程栈末尾之后的内存扩展的尺寸,来保护栈溢出。默认情况下,这被设置为PAGESIZE字节。我们可以设置guardsize线程属性为 0来禁止这个特性:在这种情况下不会有保卫缓冲被提供。同样,如果你改变stackaddr线程属性,那么系统假定我们将管理自己的栈并禁止栈保卫缓冲, 就好像我们设置guardsize线程属性为0一样。



  1. #include <pthread.h>

  2. int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrict guardsize);

  3. int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);

  4. 成功返回0,失败返回错误号。


如果guardsize线程属性被修改,操作系统可以把它四舍五入为页尺寸的倍数的一个整型。如果线程的栈指针溢出到保卫区域,那么应用将会收到一个错误,可能是一个信号。


SUS定义了几种其它的线程属性作为实时线程选项的一部份。我们将不在这里讨论。


更多的线程属性


线程有其它不被pthread_attr_t结构体表示的属性。


1、可取消状态(12.7节);


2、可取消类型(12.7节);


3、并发等级。


并 发等级控制了内核线程或进程的数量,在它们之上用护级线程被映射。如果一个实现在内核级线程和用户级线程之间保持一对一的映射,那么改变并发等级将不会有 效果,因为所用用户级线程都可能被调度。但是,如果实现在内核进程或线程之上多元化用户级线程,那么你可能可以通过增加在给定时间可以运行的用户级线程数 量,来提升性能。pthread_setconcurrency函数可以用来提供给系统所需并发等级的提示。



  1. #include <pthread.h>

  2. int pthread_getconcurrency(void);

  3. 返回当前并发级数。

  4. int pthread_setconcurrency(int level);

  5. 成功返回0,失败返回错误号。


pthread_getconcurrency函数返回当前并发等级。如果操作系统正控制着并发级数(也就是说,之前没有调用过pthread_setconcurrency),那么pthread_getconcurrency将返回0。


由 pthread_setconcurrency指定的并发级数只是对系统的一个提示。没有保证所请求的并发级数会被满足。你可以告诉系统你想它自己决定使 用的并发级数,通过传递一个0的级数。因此,一个应用可以撤消前一个非0值的pthread_setconcurrency调用的影响,通过0的级数再次 调用它。

阅读(944) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~