pthread_attr_t属性

  • 线程属性
 int pthread_attr_init(pthread_attr_t *attr);
 int pthread_attr_destroy(pthread_attr_t *attr);

如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。

int pthread_attr_getstack(const pthread_attr_t *restrict attr,void **restrict stackaddr, size_t *restrict stacksize);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t *stacksize);

这两个函数可以用于管理stackaddr线程属性,也可以管理stacksize线程属性。
对进程来说,虚拟地址空间的大小是固定的,进程中只有一个栈,所以它的大小通常不是问题。但对于线程来说,同样大小的虚拟地址空间必须被所有的线程共享。如果应用程序使用了太多的线程,致使线程栈的累计大小超过了可用的虚拟地址空间,这时就需要减少线程默认的栈大小。另一方面,如果线程调用的函数分配了大量的自动变量或者调用的函数涉及很深的栈帧,那么这时需要的栈大小可能要比默认的要大。
如果希望改变栈的默认大小,又不想自己分配地址,这时用pthread_attr_setstacksize函数就非常有用。

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

线程属性guardsize控制着线程栈末尾之后用以避免栈溢出的扩展内存的大小。这个属性默认设置为PAGESIZE个字节。可以把guardsize线程属性设为0,从而不允许属性的这种特征行为发生:在这种情况下不会提供警戒缓冲区。同样地,如果对线程属性stackaddr作了修改,系统就会假设我们会自己管理栈,并使警戒栈无效,等同于把其设为0.

  • 线程的分离状态
 int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr, int *deachstate);
 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

如果在创建线程时就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。可以使用pthread_attr_setdetachstate把线程属性detachstate设置为下面两个合法值之一:PTHREAD_CREATE_DETACHED,以分离状态启动线程;PTHREAD_CREATE_JOINABLE,正常启动线程。默认创建的线程是非分离状态的,这种情形下,原有的线程等待创建的线程结束。只有在pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。分离线程没有被其他的线程所等待,当自己运行结束时,线程也就终止了,马上释放系统资源。

  • 线程的调度
int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr,int *policy);

第1个参数是指向属性对象的指针,第2个参数是调度策略或指向调度策略的指针。调度策略可能的值是先进先出(SCHED_FIFO)、轮转法(SCHED_RR),或其它(SCHED_OTHER)。

  • 线程的继承性
int pthread_attr_getinheritsched(const pthread_attr_t*attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr,intinheritsched);

分别用来设置和得到线程的继承性。第一个参数指向属性对象的指针,第二个参数是继承性或者指向继承性的指针。继承性决定调度的参数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。 继承性的可能值是PTHREAD_INHERIT_SCHED(表示新现成将继承创建线程的调度策略和参数)和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy和schedparam属性中显式设置的调度策略和参数)。
如果你需要显式的设置一个线程的调度策略或参数,那么你必须在设置之前将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED.

  • 线程的调度参数
int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param);
int pthread_attr_setschedparam(pthread_attr_t *attr,conststruct sched_param *param);

分别用来得到和设置线程的调度参数。sched_param结构体的一个子成员sched_priority控制一个优先权值,大的有限权值对应高的优先权。系统支持的最大和最小的优先权值可以分别通过sched_get_priority_max( int policy )和sched_get_priority_min( int policy )两个函数获得。

  • 设置进程的竞争范围属性
int pthread_attr_setscope(pthread_attr_t *attr,int scope);
int pthread_attr_getscope(pthread_attr_t *attr,int *scope);

竞争返回属性定义一个线程对资源,如CPU,争夺的线程范围。第二个参数为PTHREAD_SCOPE_SYSTEM时,表示与系统中相同调度分配域下所有进程的其他线程进行竞争; 第二个参数为PTHREAD_SCOPE_PROCESS时,表示与同一个进程中同样是以PTHREAD_SCOPE_PROCESS属性创建的其他线程进行竞争。
注:更多关于线程互斥量、读写锁、条件变量的属性设置不在此文中详述,需要查阅时参考《Unix环境高级编程第2版》p318。
线程安全:如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。
如果一个函数是线程安全的,但这并不能说明对信号处理函数也是可重入的。如果函数对异步信号处理程序的重入是安全的,那么就说函数是异步-信号安全的。

int ftrylockfile(FILE *fp);
void flockfile(FILE *fp); 
void funlockfile(FILE *fp);

可以使用flockfile和ftrylockfile获取与给定FILE对象关联的锁。这个锁是递归的,当占有这把锁的时候,还可以再次获取改锁,这并不会导致死锁。

 pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));

在分配线程私有数据之前,需要创建与该数据关联的键。这个键将用于获取对线程私有数据的访问权。
创建的键存放在keyp指向的内存单元,这个键可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据地址相关联。创建新键时,每个线程的数据地址设为null值。
除了创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程退出时,如果数据地址已经被置为非null值,那么析构函数会被调用。当线程调用pthread_exit或者线程执行返回,正常退出时,会被调用;但线程如果调用了exit,_exit,_Exit,abort等非正常退出时,就不会调用析构函数。
线程通常使用malloc为线程私有数据分配空间,析构函数通常释放已分配的内存,如果线程没有释放内存就退出了,那么线程所属进程出现了内存泄漏。

 int pthread_key_delete(pthread_key_t *key);

对所有的线程,都可以通过调用pthread_key_delete来取消键与线程私有数据值之间的关联关系。

 void *pthread_getspecific(pthread_key_t key);
 int pthread_setspecific(pthread_key_t, key, const void *value);

键一旦创建,就可以通过调用pthread_setspecific把键和线程私有数据关联起来。可以通过pthread_getspecific函数获得线程私有数据的地址。如果没有线程私有数据值与键关联,pthread_getspecific将返回一个空指针,可以据此确定是否需要调用pthread_setspecific。

 int pthread_sigmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);

pthread_sigmask函数与sigprocmask函数基本相同,除了pthread_sigmask工作在线程中,并且失败时返回错误码,而不像sigprocmask中那样设置errno并返回-1。

 int sigwait(const sigset_t *restrict set, int *restrict signop);

线程可以通过调用sigwait等待一个或多个信号发生。

 int pthread_kill(pthread_t thread, int signo);

要把信号发送到进程,可以调用kill;要把信号发送给线程可以调用pthread_kill。可以传一个0值的signo来检查线程是否存在。如果信号的默认处理动作是终止该进程,那么把信号传递给某个线程仍然会杀掉整个进程。

 int pthrad_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));

子进程通过继承整个地址空间的副本,也从父进程那里继承了所有互斥量、读写锁、和条件变量的状态栏。如果父进程包含多个线程,子进程在fork返回以后,如果紧接着不是马上exec的话就需要清理锁状态。
用pthread_atfork函数最多可以安装三个帮助清理锁的函数。prepare fork处理程序由父进程在fork创建子进程之前调用,这个fork处理程序的任务是获取父进程定义的所有锁。parent fork处理程序是在fork创建了子进程以后,但在fork返回之前在父进程环境中调用的,这个fork处理程序的任务是对parent fork处理程序获得的所有锁进行解锁。child fork处理程序在fork返回之前在子进程环境中调用,与parent fork一样,child fork处理程序也必须释放prepare fork处理程序获得的所有锁。

Leave a Reply

Your email address will not be published. Required fields are marked *