Solaris多线程编程(3)
3 线程属性前面的章节描述了用缺省属性创建线程的基本概念。这一章讨论在创建线程时如何设置线程的属性。注意只有在POSIX标准中才有线程属性和退出线程的概念,因此这一章中描述的API函数只适用于POSIX线程。除此之外,Solaris线程接口和POSIX线程接口基本上是一样的(参见第9章对这两种接口的相同之处和差别的描述)。l 初始化属性l 释放属性对象l 置分离状态l 取分离状态l 置堆栈防护区大小l 取堆栈防护区大小l 置域l 取域l 置线程并发级别l 取线程并发级别l 置调度策略l 取调度策略l 置继承调度策略l 取继承调度策略l 置调度参数l 取调度参数l 置堆栈大小l 取堆栈大小l 关于堆栈l 置堆栈地址l 取堆栈地址属性属性对象是被用来指定线程的属性的。当用pthread_create(3T)函数创建一个线程或初始化一个同步变量时,都需要使用属性对象。一般情况下,使用缺省的属性对象就行了。注意:只能在创建线程的时候指定线程的属性,不能在线程运行时改变它的属性。属性对象是不透明的(数据结构不公开),一般不能用赋值语句直接对它进行修改。有一组专门的函数对它们进行初始化、配置和释放。一旦属性对象被初始化并被配置好了。它在进程范围内都是有效的。最好在程序刚开始运行时就配置好所有必须的属性对象,当需要时再使用。使用属性对象有两个主要的好处。l 首先它提高了程序的可移植性。在符合POSIX多线程标准的不同系统中,属性对象的内部结构有可能不同,但用来操作属性对象的函数接口并没有变化,使用属性对象的程序不需要为了属性对象内部的变化而改变程序的代码。如果移植的目标系统支持目前系统中不支持的属性对象,那么,移植时必须修改相关的代码,但这种修改是很简便的,因为属性对象只需要初始化一次。l 其次简化了对线程属性的指定。有这样一个例子,设想在一个进程中有多组线程,每一组都提供了一种不同的服务,因此每一组都必须有它自己的属性。在程序开始的时候,可以为每一组线程创建一个线程属性对象。将来创建线程时可以使用相应的属性对象。初始化属性对象的过程很简单,可以集中在程序开始的一小段代码中。在创建相应的线程之前还可以方便的对它们进行修改。需要注意的是,如果不释放属性对象,它在进程退出前一直是有效的。当一个属性对象被初始化时,系统为这个对象分配内存。这些内存必须被返还给系统。POSIX线程标准提供了释放属性对象的函数。初始化属性pthread_attr_init(3T)pthread_attr_init(3T)初始化一个线程属性对象,并将其属性值置成缺省值。它占用的内存由线程库分配。函数原型:int pthread_attr_init( pthread_attr_t *tattr );#include pthread_attr_t tattr;int ret;/* initialize an attribute to the default value */ret = pthread_attr_init( &tattr );属性对象的缺省值如下表:表3-1 属性对象的缺省值属性值结果scope(线程域)PTHREAD_SCOPE_PROCESS线程是非绑定的,即不是永久的绑定在LWP上Detachstate(分离状态)PTHREAD_CREATE_JOINABLE线程退出后,线程退出代码和线程都将暂时保留Stackaddr(堆栈地址)NULL线程的堆栈由系统分配Stacksize(堆栈大小)1M bytes线程的堆栈大小由系统决定priority(优先级)线程从父线程继承线程的优先级Inheritsched(继承调度优先级)PTHREAD_INHERIT_SCHED线程继承父线程的调度优先级schedpolicy(调度策略)SCHED_OTHER线程根据优先级调度,线程保持运行,直到更高优先级的线程强占了处理器资源,或是线程被阻塞,或是线程主动出让运行权返回值pthread_attr_init()函数成功后,返回一个0。任何其他返回值都表示有错误发生。当如下情况发生时,pthread_attr_init()函数失败并返回相应的错误代码。ENOMEM 没有足够的内存来初始化线程属性对象。释放属性对象pthread_attr_destroy(3T)pthread_attr_destroy(3T)用来删除属性对象占用的内存。调用这个函数后,相应的属性对象无效。函数原型:int pthread_attr_destroy( pthread_attr_t *tattr );#include pthread_attr_t tattr;int ret;/* destroy an atribute */ret = pthread_attr_destroy( &tattr );返回值pthread_attr_destroy()函数成功后,返回一个0。任何其他返回值都表示有错误发生。当如下情况发生时,pthread_attr_destroy()函数失败并返回相应的错误代码。EINVAL tattr的值非法。设置分离状态pthread_attr.setdetachstate(3T)创建线程时,如果指定这个线程为分离线程(PTHREAD_CREATE_DETACHED),那么一旦这个线程终止以后,它的线程标识符和其他相关的资源可以立即被使用。如果不需要等待某个线程的终止,那么可以使用pthread_attr_setdetachstate(3T)函数将这个线程指定为分离的。创建线程时,如果指定这个线程为非分离线程(PTHREAD_CREATE_JOINABLE),那么将会有另一个线程等待这个线程的结束,也就是说,会有另一个线程调用pthread_join()等待这个线程的结束。不论线程是分离的或非分离的,进程将在所有线程都退出后才退出。参见“线程的终止”中对main()函数退出造成进程退出的描述。函数原型:int pthread_attr_setdetachstate( pthread_attr_t *tattr, int detachstate );#include pthread_attr_t tattrint ret;/* set the thread detach state */ret = pthread_attr_setdetachstate( &tattr, PTHREAD_CREATE_DETACHED );注意:如果没有有效的同步措施,一个刚创建的分离线程有可能在pthread_create()函数返回之前就终止了,而它的线程标识符则被另一个新线程所使用。对于非分离的(PTHREAD_CREATE_JOINABLE)线程,必须由别的线程调用pthread_join函数来等待它的终止。pthread_join函数将释放这个终止的线程,这是十分重要的。因为,如果不调用pthread_join函数释放这个终止的线程,那么这个线程占用的资源将一直被占用,无法被新线程使用,从而造成内存泄露。所以,当你不需要等待一个线程的终止时,请使用分离线程。例子程序3-1 分离线程#include pthread_attr_t tattr;pthread_t tid;void (*startingroute )( void *arg );int ret;/* initialize with default attributes */ret = pthread_attr_init( &tattr );ret = pthread_attr_setdetachstate( &tattr, PTHREAD_CREATE_DETACHED );ret = pthread_create( &tid, &tattr, startingroute, NULL );返回值pthread_attr_setdetachstate()函数成功后,返回一个0。任何其他返回值都表示有错误发生。当如下情况发生时,pthread_attr_setdetachstate()函数失败并返回相应的错误代码。EINVAL tattr的值或detachstate的值非法。取分离状态pthread_attr_getdetachstate(3T)pthread_attr_getdetachstate(3T)函数可以取线程分离状态:分离的或是非分离的。函数原型:int pthread_attr_getdetachstate( const pthread_attr_t *tattr, int *detachstate );#include pthread_attr_t tattr;int detachstate;int ret;/* get detachstate of thread */ret = pthread_attr_getdetachstate( &tattr, &detachstate );返回值pthread_attr_getdetachstate()函数成功后,返回一个0。任何其他返回值都表示有错误发生。当如下情况发生时,pthread_attr_getdetachstate()函数失败并返回相应的错误代码。EINVAL tattr的值或detachstate的值非法。设置堆栈防护区大小pthread_attr_setguardsize(3T)pthread_attr_setguardsize(3T)设置线程属性对象的guardsize参数。堆栈保护区被用来在堆栈指针越界的情况下提供保护。如果一个线程具有堆栈保护的特性,那么系统在创建线程堆栈时会在堆栈的末尾多分配一块内存,用来防止指针访问堆栈时,溢出堆栈的边界。如果一个应用程序访问堆栈时溢出到堆栈保护区将会引发一个错误。(往往是当前线程收到一个SIGSEGV信号)提供堆栈保护区属性有两个原因:1. 首先,堆栈保护会引起系统资源浪费。如果一个应用程序创建了大量线程,而且确保这些线程不会越界访问堆栈,那么这个应用程序可以通过取消堆栈保护区来节省系统资源。2. 当线程在堆栈中存放大的数据结构时,有可能需要一个大的堆栈保护区。如果线程属性对象的guardsize参数值为0,那么创建线程时将不会创建堆栈保护区,如果线程属性对象的guardsize参数值大于0,那么使用这个线程属性对象创建的线程将起码有一个guardsize大小的堆栈保护区。缺省情况下,线程的堆栈保护区大小根据系统版本的不同而各不相同。创建线程堆栈时,一般的做法是将guardsize参数的值上舍入成系统变量PAGESIZE(参见sys/mman.h)的整数倍,来分配堆栈保护区。尽管实际的堆栈保护区大小往往是guardsize参数的上舍入值、PAGESIZE的整数倍。但程序调用pthread_attr_getguardsize()函数来取堆栈保护区的大小时,获得的堆栈保护区大小将是调用pthread_attr_setguardsize()函数时指定的guardsize参数值。#include int pthread_attr_setguardsize( pthread_attr_t *attr, size_t guardsize );返回值在下列情况下,pthread_attr_setguardsize函数失败返回:EINVAL 参数attr, guardsize的值非法。取堆栈防护区大小pthread_attr_getguardsize(3T)pthread_attr_getguardsize(3T)函数被用来取线程属性对象的堆栈保护区大小。创建线程堆栈时,一般的做法是,将guardsize参数的值上舍入成系统变量PAGESIZE(参见sys/mman.h)的整数倍,来分配线程的堆栈保护区。尽管实际的堆栈保护区的大小往往guardsize参数的上舍入值,PAGESIZE的整数倍。但程序调用pthread_attr_getguardsize()函数来获取堆栈保护区的大小时,获得的堆栈保护区大小将是调用pthread_attr_setguardsize()函数时指定的guardsize参数值。#include int pthread_attr_getguardsize( const pthread_attr_t *attr, size_t *guardsize );返回值在下列情况下,pthread_attr_getguardsize函数失败返回:EINVAL 参数attr, guardsize的值非法。设置域pthread_attr_setscope(3T)pthread_attr_setscope(3T)函数用来指定将来创建的线程是绑定(PTHREAD_SCOPE_SYSTEM)的还是非绑定的(PTHREAD_SCOPE_PROCESS)。注意:在一个进程中可以同时有这两种不同类型的线程。函数原型:int pthread_attr_setscope( pthread_attr_t *tattr, int scope );#include pthread_attr_t tattr;int ret;/* bound thread */ret = pthread_attr_setscope( &tattr, PTHREAD_SCOPE_SYSTEM );/* unbound thread */ret = pthread_attr_setscope( &tattr, PTHREAD_SCOPE_PROCESS );注意,在这个例子中,一个是关于初始化属性;另一个是从缺省属性设置变量;再一个是创建pthread。#include pthread_attr_t attr;pthread_t tid;void start_routine;void arg;int ret;/* initialized with default atributes */ret = pthread_attr_init (&tattr);/* BOUND behavior */ret = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);ret = pthread_create (&tid, &tattr, start_routine, arg);返回值pthread_attr_setscope()函数成功后,返回一个0。任何其他返回值都表示有错误发生。当如下情况发生时,pthread_attr_setscope()函数失败并返回相应的错误代码。EINVAL tattr的值非法。取域pthread_attr_getscope(3T)pthread_attr_getscopy(3T)函数用来取线程的域(绑定的或是非绑定的)。函数原型:int pthread_attr_getscope( pthread_attr_t *tattr, int *scope );#include pthread_attr_t tattr;int scope;int ret;/* get scope of thread */ret = pthread_attr_getscope( &tattr, &scope );返回值pthread_attr_getscope()函数成功后,返回一个0。任何其他返回值都表示有错误发生。当如下情况发生时,pthread_attr_getscope()函数失败并返回相应的错误代码。EINVAL tattr的值非法或scope为空指针。设置线程并发级别pthread_setconcurrency(3T)在一个进程中,可能需要多个非绑定的线程被同时激活(即绑定到LWP上参与核心的并发调度)。缺省情况下,线程库保证有足够多的线程处在激活状态下以确保进程的运行。尽管这样可以节省系统资源,但并不一定能得到最有效的并发性。pthread_setconcurrency(3T)函数允许一个应用程序通知线程库它所需要的并行级别(即同一时刻最多可以有多少个非绑定线程处在激活状态下)。调用这个函数后,应用程序实际的并发级别并不一定(参见 “thr_setconcurrency(3T)”这一节关于Solaris线程的相关描述)。如果参数new_level的值为0,并行级别将保持不变,就像pthread_setconcurrency(3T)函数从来没有被调用过。当一个应用程序调用pthread_setconcurrency(3T)函数时,它只是通知系统它所需要的并发级别。系统将它作为一种提示,而不是命令。#include int pthread_setconcurrency( int new_level );返回值在下列情况下,pthread_setconcurrency函数失败返回:EINVAL 参数new_level的值非法。发布人:gloomy 来自: