线程限制和system的name参数
限制名称 | 描述 | name参数 |
PTHREAD_DESTRUCTOR_ITERATIONS | 线程退出时操偏系统实现试图锁毁私有数据的最大次数 | _SC_THREAD_DESTRUCTOR_ITERATIONS |
PTHREAD_KEYS_MAX | 进程可以创建的键的最大数目 | _SC_THREAD_KEYS_MAX |
PTHREAD_STACK_MIN | 一个线程的栈可用的最小字节数 | _SC_THREAD_STACK_MIN |
PTHREAD_THREADS_MAX | 进程可以创建的最大线程数 | _SC_THREAD_PTHREADS_MAX |
在调用pthread_create创建线程时可通过传入pthread_attr_t结构的指针来修改线程的默认属性。
pthread_attr_t结构是不透明的,在使用前需要用pthread_attr_init函数初始化,使用完后则要用pthread_attr_destroy来去初始化。
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); // 两者在成功时都返回0,失败时都返回错误码
POSIX的线程属性包括以下几个:
名称 | 描述 |
detachstate | Detached or joinable state(线程的分离状能属性) |
inheritsched | Scheduling inheritance(线程调度方式继承方式) |
schedpolicy | Scheduling policy(线程的调度策略 |
schedparam | Scheduling parameters(调度策略相应的参数) |
contentionscope | Scheduling contention scope(线程调度竞争范围) |
stackaddr | Stack address(线程栈的最低地址) |
stacksize | Stack size(线程栈的大小) |
guardsize | Stack guard (overflow) size(线程末尾的警戒缓冲区的大小) |
detachstate有以下两个合法值:
PTHREAD_CREATE_DETACHED:以分离状态启动线程,若不需要以pthread_join来等待或是获取线程的终止状态,则可以以分离状态启动线程。也可以在线程运行后调用函数pthread_detach使线程进入分离状能。
PTHREAD_CREATE_JOINABLE:这昰默认属性。以此属性创建的线程在终止时可调用pthread_join获取其终止状态。
相应的set和get函数分别可以设置或获取detachstate的值。
#include <pthread.h> int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *detachstate); int pthread_attr_getdetachstate(pthread_attr_t *attr, int detachstate); // 两者都是成功返回0,失败返回错误代码
二、线程栈属性设置
#include <pthread.h> int pthread_attr_getstack(const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack(const pthread_attr_t *attr, void *stackaddr, size_t stacksize); // 两者的返回值都是成功返回0,失败返回错误码。
pthread_attr_getstack和pthread_attr_setstack分别用来获取和设置栈属性。
对于进程来说,虚拟地址的空间大小是固定的,进程中只有一个栈,所以它的大小通常不是问题。但对线程来说 同样大小的虚拟空间必须阁所有线程共享。如果应用程序使用了大多线程,致使线程栈的累计大小超过了可用的虚拟地址空间,这时就需要减少线程栈默认的栈大小。另一方面,如果线程调用的函数分配了木量的自动变量或者调用的函数涉及很深的栈帧,那么这时就需要栈的大小比默认的大。
pthread_attr_setstack用来改变新建线程的栈位置。线程栈所占内存范围可寻址的最低地址可以由stackaddr参数指定
#include <pthread.h> int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t satcksize); // 两者都是在成功时返回0,失败时返回错误代码
应用程序也可以用pthread_attr_getstacksize和pthread_atr_setstacksize函数读取或设置线程属性stacksize。
如果希望改变栈的默认大小又不希望自己处理线程栈的分配问题,这时可以使用pthread_attr_settacksize。
#include <pthread.h> int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, size_t *restrct guardsize); int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); // 两者都是成功返回0,失败返回错误编码
guradsize控制着线程栈末尾之后用以避免溢出的扩展内存的大小。这个属性默认是PAGESIZE个字节。可以将guardsize设为0,这样就不提异警戒缓冲。如果设置了stackaddr属性,系统会假设我们自己管理栈,并使警戒缓冲机制失效。
如果guardsize线程属性被修改了,操作系统可能将其取为页大小的整数倍。如果线程的栈指针溢出到了警戒区域,应用程序就可能会收到出错信号。
#include <pthread.h> int pthread_attr_getinheritsched(const pthread_attr_t *restrict attr, int *restruct inheritsched); int pthread_attr_setinheritsched(pthread_attr *attr, int inheritsched); // 如果成功两者都返回0,失败都返回错误码
上面两个函数用来获取和设置inheritsched属性。inheritsched属性决定了在创建线程时其他调度属性怎样被设置。
PTHREAD_INHERIT_SCHED:指明线程的调度属性应该继承自创建它的线程。当指定了该值时,其他的线程调度属性将被忽略。
PTHREAD_EXPLICIT_SCHED:指明线程的调度属性由创建线程时的属性值指定。
线程的调度属性有:调度策略(scheduling policy),对应schedpolicy;调度参数(scheduling parameters),对应于schedparam;以及调度竞争范围(scheduling contention scope),对应于contentionscope。
调度策略由pthread_attr_setschedpolicy来设置。
#include <pthread.h> int pthread_attr_getschedpolicy(const pthread_attr_t *restrict attr, int *restrict policy); int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); // 若成功都返回0,失败返回错误码
上面两个函数分别用来获取和设置调度策略。
policy的值可以是SCHED_FIFO、SCHED_RR和SCHED_OTHER中之一。这三个常量在<sched.h>中定义。
SCHED_FIFO:先进先出策略,先进先出 (FIFO) 调度。每个线程都有一个固定的优先级;当多个线程具有相同的优先级时,它们按照先进先出 (FIFO) 的顺序运行直到完成。
SCHED_RR:循环 (RR) 调度。每个线程都有固定的优先级;当多个线程具有相同的优先级时,它们按照先进先出 (FIFO) 的顺序在一个 固定的时间片内运行。
SCHED_OTHER:缺省的 调度。每个线程都有一个由调度程序根据线程的活动动态修改的初始优先级;线程的执行是按时间分割的。在其他系统上,这个调度策略可能会不同。
当一个线程以调度策略SCHED_FIFO、SCHED_RR、或是SCHED_SPORADIC运行时,在等待同一个互斥变量时,当互斥量解锁时,优先级较高的将会优获得。(这段话是从linux的man手册上翻译过来的,不知道SCHED_SPORADIC是何种策略,而手册上也未提到可以设置线程的调度策略可以为它。大概是Linux的默认调度策略吧,即SCHED_OTHER所指的策略。)
#include <pthread.h> int pthread_attr_getschedparam(const pthread_attr_t *restric attr, struct sched_param *restrict param); int pthread_attr_setschedparam(pthread_attr_t *restrict attr, const struct sched_param *restrict param); // 成功时都返回0,失败时返回错误码
上面两个函数用来设置或是获取参数attr中的调度参数属性(scheduling parameter attributes)。sched_param结构在<sched.h>头文件中定义。对于SCHED_FIFO和SCHED_RR策略只需要其成员sched_priority。
而对于SCHED_SPORADIC策略,则需要其成员sched_priority、sched_ss_low_priority、sched_ss_repl_period、sched_ss_init_budget和sched_ss_max_repl。其中sched_ss_repl_period的值必须大于或等于sched_ss_init_budget的值。sched_ss_max_repl的值必须在区间[1, {SS_REPL_MAX}]中。(PS:SCHED_SPORADIC策略是Linux特有的,我在FreeBSD9.1上查看sched.h的源文件,並未发现其定义,而且FreeBSD9.1上sched_param结构只有一个成员,即sched_priority。)
#include <pthread.h> int pthread_attr_getscope(const pthread_attr_t *restrict attr, int *restrict contentionscope); int pthread_attr_setscope(pthread_attr_t *restrict attr, int contentionscope);
上面两个函数获取或设置线程的调度竞争范围。
contentionscope属性可能的值为以下两个:
PTHREAD_SCOPE_SYSTEM:表示在系统级上竞争资源。
PTHREAD_SCOPE_PROCESS:表示在进程内竞争资源。
PS:我在linux和FreeBSD上,设置线程的调度属性都必须以root身分运行,否则在创建线程时会因没权限而出错。
关于线程调度属性的更多内容,可以参考下面两个链接:
调度线程
Posix多线程编程—线程属性
互斥量属性由pthread_mutexattr_t结构表示,用pthread_mutexattr_init初始化,pthread_mutexattr_destroy反初始化。
#include <pthread.h> int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); // 两者都是成功返回0,失败反回错误编码
pthread_mutexattr_init用默认的互斥量属性初始化pthread_mutexattr_t结构。
比较重要的两个属性是进程共享属性和类型属性。
在POSIX.1中进程共享属性是可选的,可用_POSIX_THREAD_PROCESS_SHARED或是运行时用sysconf函数检查_SC_THREAD_PROCESS_SHARED来确认系统是否支持。
进程共享属性可设为下面两个:
PTHREAD_PROCESS_PRIVATE:这是默认的,表示互斥量只在进程内访问。
PTHREAD_PROCESS_SHARED:表示从多个进程共享内存分配的互斥量就可用于多个进程的同步。
在后面的章节中将说明,存在允许互相独立的多个进程把同一个内存区域映射到它们各自独立的空间中的机制。如此以来,多个进程在访问共享数据时就需要同步,如果互斥量属性设为PTHREAD_PROCESS_SHARED,就允许多个进程用其来同步。
互斥量属性设为PTHREAD_PROCESS_PRIVATE时允许pthread线程库提供更加有效的实现。
pthread_mutexattr_getpshared用来获取共享属性,pthread_mutexattr_setpshared用来设置共享属性。
#include <pthread.h> int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared); int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); // 两个函数都是在成功时返回0,失败时返回错误代码
POSIX.1定义了四种互斥量类型:
PTHREAD_MUTEX_NORMAL:标准的互斥量类型,不提供任何特殊的错误检查或死锁检查。
PTHREAD_MUTEX_RECURSIVE:允许同一线程在互斥量解锁前对其进行多次加锁。在解锁的次数和加锁次数不同时不会释放锁。
PTHREAD_MUTEX_ERRORCHECK:对互斥量进行错误或是死锁检查。
PTHREAD_MUTEX_DEFAULT:可以用以请求默认语义。操作系统在实现它的时候将其自由映射为其他类型。
互斥量类型的行为
互斥量类型 | 没有解锁时再加锁 | 不占用锁时解锁 | 在己解锁时解锁 |
PTHREAD_MUTEX_NORMAL | 死锁 | 未定义 | 未定义 |
PTHREAD_MUTEX_ERRORCHECK | 返回错误 | 返回错误 | 返回错误 |
PTHREAD_MUTEX_RECURSIVE | 允许 | 返回错误 | 返回错误 |
PTHREAD_MUTEX_DEFAULT | 未定义 | 未定义 | 未定义 |
#include <pthread.h> int pthread_mutexattr_gettype(const pthread_mutexattr_t * restrict attr, int *restrict type); int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); // 两者在成功时都返回0,失败时返回错误代码
注意:在互斥量用于保护与条件变量关联的条件时,在阻塞线程之前,pthread_cond_wait和pthread_cond_timewait函数释放与条件相关的互斥量,这就允许其他线程获取互斥量、改变条件、释放互斥量并向条件变量发送信号。既然改变条件时必须占有互斥量,所以使用递归锁并不是好的办法。如果递归互斥量被多次加锁,然后用在调用pthread_cond_wait函数中,那么条件永远都得不到满足,因为pthread_cond_wait所做的解锁操作并不能释放互斥量。
pthread_rwlockattr_t结构表示读写锁属性。
pthread_rwlockattr_init用于初始化读写锁属性,pthread_rwlockattr_destroy则回收结构。
#include <pthread> int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr); // 两者都是在成功时返回0,失败时返回错误码
读写锁支持的唯一属性是进程共享属性,该属性与互斥量的进程共享属生相同。下面的两个函数用于读取和设置读写锁的进程共享属性。
#include <pthread.h> int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restric attr, int *restrict pshared); int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);
条件变量属性与读写锁类似。由pthread_condattr_t表示。
下面的函数初始化和回收该属性。
#include <pthread.h> int pthread_condattr_init(pthread_condattr_t *attr); int pthread_condattr_destroy(pthread_condattr_t *attr);
下面的函数取得和设置共享属性。
#include <pthread.h> int pthread_condattr_getpshared(const pthread_condattr_t * restrict attr, int *restrict pshared); int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared);