4. 线程的属性
前面还说到过线程创建的时候是有属性的,这个属性由一个线程属性对象来描述。线程属性对象由pthread_attr_init()接口初始化,并由pthread_attr_destory()来销毁,它们的完整定义是:
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destory(pthread_attr_t *attr);
那么线程拥有哪些属性呢?一般地,Linux下的线程有:绑定属性、分离属性、调度属性、堆栈大小属性和满占警戒区大小属性。下面我们就分别来介绍这些属性。
4.1 绑定属性
说到这个绑定属性,就不得不提起另外一个概念:轻进程(Light Weight Process,简称LWP)。轻进程和Linux系统的内核线程拥有相同的概念,属于内核的调度实体。一个轻进程可以控制一个或多个线程。默认情况下,对于一个拥有n个线程的程序,启动多少轻进程,由哪些轻进程来控制哪些线程由操作系统来控制,这种状态被称为非绑定的。那么绑定的含义就很好理解了,只要指定了某个线程“绑”在某个轻进程上,就可以称之为绑定的了。被绑定的线程具有较高的相应速度,因为操作系统的调度主体是轻进程,绑定线程可以保证在需要的时候它总有一个轻进程可用。绑定属性就是干这个用的。
设置绑定属性的接口是pthread_attr_setscope(),它的完整定义是:
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
它有两个参数,第一个就是线程属性对象的指针,第二个就是绑定类型,拥有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。代码2演示了这个属性的使用。
#include
#include
……
int main( int argc, char *argv[] )
{
pthread_attr_t attr;
pthread_t th;
……
pthread_attr_init( &attr );
pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
pthread_create( &th, &attr, thread, NULL );
……
}
代码2设置线程绑定属性
不知道你是否在这里发现了本文的矛盾之处。就是这个绑定属性跟我们之前说的NPTL有矛盾之处。在介绍NPTL的时候就说过业界有一种m:n的线程方案,就跟这个绑定属性有关。但是笔者还说过NPTL因为Linux的“蠢”没有采取这种方案,而是采用了“1:1”的方案。这也就是说,Linux的线程永远都是绑定。对,Linux的线程永远都是绑定的,所以PTHREAD_SCOPE_PROCESS在Linux中不管用,而且会返回ENOTSUP错误。
既然Linux并不支持线程的非绑定,为什么还要提供这个接口呢?答案就是兼容!因为Linux的NTPL是号称POSIX标准兼容的,而绑定属性正是POSIX标准所要求的,所以提供了这个接口。如果读者们只是在Linux下编写多线程程序,可以完全忽略这个属性。如果哪天你遇到了支持这种特性的系统,别忘了我曾经跟你说起过这玩意儿:)
4.2 分离属性
前面说过线程能够被合并和分离,分离属性就是让线程在创建之前就决定它应该是分离的。如果设置了这个属性,就没有必要调用pthread_join()或pthread_detach()来回收线程资源了。
设置分离属性的接口是pthread_attr_setdetachstate(),它的完整定义是:
pthread_attr_setdetachstat(pthread_attr_t *attr, int detachstate);
它的第二个参数有两个取值:PTHREAD_CREATE_DETACHED(分离的)和PTHREAD_CREATE_JOINABLE(可合并的,也是默认属性)。代码3演示了这个属性的使用。
#include
#include
……
int main( int argc, char *argv[] )
{
pthread_attr_t attr;
pthread_t th;
……
pthread_attr_init( &attr );
pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
pthread_create( &th, &attr, thread, NULL );
……
}
代码3设置线程分离属性
4.3 调度属性
线程的调度属性有三个,分别是:算法、优先级和继承权。
Linux提供的线程调度算法有三个:轮询、先进先出和其它。其中轮询和先进先出调度算法是POSIX标准所规定,而其他则代表采用Linux自己认为更合适的调度算法,所以默认的调度算法也就是其它了。轮询和先进先出调度算法都属于实时调度算法。轮询指的是时间片轮转,当线程的时间片用完,系统将重新分配时间片,并将它放置在就绪队列尾部,这样可以保证具有相同优先级的轮询任务获得公平的CPU占用时间;先进先出就是先到先服务,一旦线程占用了CPU则一直运行,直到有更高优先级的线程出现或自己放弃。
设置线程调度算法的接口是pthread_attr_setschedpolicy(),它的完整定义是:
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
它的第二个参数有三个取值:SCHED_RR(轮询)、SCHED_FIFO(先进先出)和SCHED_OTHER(其它)。
Linux的线程优先级与进程的优先级不一样,进程优先级我们后面再说。Linux的线程优先级是从1到99的数值,数值越大代表优先级越高。而且要注意的是,只有采用SHCED_RR或SCHED_FIFO调度算法时,优先级才有效。对于采用SCHED_OTHER调度算法的线程,其优先级恒为0。
设置线程优先级的接口是pthread_attr_setschedparam(),它的完整定义是:
struct sched_param {
int sched_priority;
}
int pthread_attr_setschedparam(pthread_attr_t *attr, struct sched_param *param);
sched_param结构体的sched_priority字段就是线程的优先级了。
此外,即便采用SCHED_RR或SCHED_FIFO调度算法,线程优先级也不是随便就能设置的。首先,进程必须是以root账号运行的;其次,还需要放弃线程的继承权。什么是继承权呢?就是当创建新的线程时,新线程要继承父线程(创建者线程)的调度属性。如果不希望新线程继承父线程的调度属性,就要放弃继承权。
设置线程继承权的接口是pthread_attr_setinheritsched(),它的完整定义是:
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
它的第二个参数有两个取值:PTHREAD_INHERIT_SCHED(拥有继承权)和PTHREAD_EXPLICIT_SCHED(放弃继承权)。新线程在默认情况下是拥有继承权。
代码4能够演示不同调度算法和不同优先级下各线程的行为,同时也展示如何修改线程的调度属性。
#include
#include
#include
#include
#define THREAD_COUNT 12
void show_thread_policy( int threadno )
{
int policy;
struct sched_param param;
pthread_getschedparam( pthread_self(), &policy, ¶m );
switch( policy ){
case SCHED_OTHER:
printf( "SCHED_OTHER %d\n", threadno );
break;
case SCHED_RR:
printf( "SCHDE_RR %d\n", threadno );
break;
case SCHED_FIFO:
printf( "SCHED_FIFO %d\n", threadno );
break;
default:
printf( "UNKNOWN\n");
}
}
void* thread( void *arg )
{
int i, j;
long threadno = (long)arg;
printf( "thread %d start\n", threadno );
sleep(1);
show_thread_policy( threadno );
for( i = 0; i
for( j = 0; j
printf( "thread %d\n", threadno );
}
printf( "thread %d exit\n", threadno );
return NULL;
}
int main( int argc, char *argv[] )
{
long i;
pthread_attr_t attr[THREAD_COUNT];
pthread_t pth[THREAD_COUNT];
struct sched_param param;
for( i = 0; i
pthread_attr_init( &attr[i] );
for( i = 0; i
param.sched_priority = 10;
pthread_attr_setschedpolicy( &attr[i], SCHED_FIFO );
pthread_attr_setschedparam( &attr[i], ¶m );
pthread_attr_setinheritsched( &attr[i], PTHREAD_EXPLICIT_SCHED );
}
for( i = THREAD_COUNT / 2; i
param.sched_priority = 20;
pthread_attr_setschedpolicy( &attr[i], SCHED_FIFO );
pthread_attr_setschedparam( &attr[i], ¶m );
pthread_attr_setinheritsched( &attr[i], PTHREAD_EXPLICIT_SCHED );
}
for( i = 0; i
pthread_create( &pth[i], &attr[i], thread, (void*)i );
for( i = 0; i
pthread_join( pth[i], NULL );
for( i = 0; i
pthread_attr_destroy( &attr[i] );
return 0;
}
代码4设置线程调度属性
这段代码中含有一些没有介绍过的接口,读者们可以使用Linux的联机帮助来查看它们的具体用法和作用。
4.4 堆栈大小属性
从前面的这些例子中可以了解到,线程的主函数与程序的主函数main()有一个很相似的特性,那就是可以拥有局部变量。虽然同一个进程的线程之间是共享内存空间的,但是它的局部变量确并不共享。原因就是局部变量存储在堆栈中,而不同的线程拥有不同的堆栈。Linux系统为每个线程默认分配了8MB的堆栈空间,如果觉得这个空间不够用,可以通过修改线程的堆栈大小属性进行扩容。
修改线程堆栈大小属性的接口是pthread_attr_setstacksize(),它的完整定义为:
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
它的第二个参数就是堆栈大小了,以字节为单位。需要注意的是,线程堆栈不能小于16KB,而且尽量按4KB(32位系统)或2MB(64位系统)的整数倍分配,也就是内存页面大小的整数倍。此外,修改线程堆栈大小是有风险的,如果你不清楚你在做什么,最好别动它(其实我很后悔把这么危险的东西告诉了你:)。
4.5 满栈警戒区属性
既然线程是有堆栈的,而且还有大小限制,那么就一定会出现将堆栈用满的情况。线程的堆栈用满是非常危险的事情,因为这可能会导致对内核空间的破坏,一旦被有心人士所利用,后果也不堪设想。为了防治这类事情的发生,Linux为线程堆栈设置了一个满栈警戒区。这个区域一般就是一个页面,属于线程堆栈的一个扩展区域。一旦有代码访问了这个区域,就会发出SIGSEGV信号进行通知。
虽然满栈警戒区可以起到安全作用,但是也有弊病,就是会白白浪费掉内存空间,对于内存紧张的系统会使系统变得很慢。所有就有了关闭这个警戒区的需求。同时,如果我们修改了线程堆栈的大小,那么系统会认为我们会自己管理堆栈,也会将警戒区取消掉,如果有需要就要开启它。
修改满栈警戒区属性的接口是pthread_attr_setguardsize(),它的完整定义为:
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
它的第二个参数就是警戒区大小了,以字节为单位。与设置线程堆栈大小属性相仿,应该尽量按照4KB或2MB的整数倍来分配。当设置警戒区大小为0时,就关闭了这个警戒区。
虽然栈满警戒区需要浪费掉一点内存,但是能够极大的提高安全性,所以这点损失是值得的。而且一旦修改了线程堆栈的大小,一定要记得同时设置这个警戒区。
五、线程终止
pthread_exit函数:
原型: void pthread_exit(void *rval_ptr);
头文件:
参数: rval_ptr是一个无类型指针, 指向线程的返回值存储变量.
pthread_join函数:
原型: int pthread_join(pthread_t thread, void **rval_ptr);
头文件:
返回值: 成功则返回0, 否则返回错误编号.
参数:
thread: 线程ID.
rval_ptr: 指向返回值的指针(返回值也是个指针).
说明:
调用线程将一直阻塞, 直到指定的线程调用pthread_exit, 从启动例程返回或被取消.
如果线程从它的启动例程返回, rval_ptr包含返回码.
如果线程被取消, 由rval_ptr指定的内存单元置为: PTHREAD_CANCELED.
如果对返回值不关心, 可把rval_ptr设为NULL.
实例:
1 #include
2 #include
3
4 /*print process and thread IDs*/
5 void printids(const char *s)6 {7 pid_t pid, ppid;8 pthread_t tid;9 pid=getpid();10 ppid =getppid();11 tid =pthread_self();12 printf("%16s pid %5u ppid %5u tid %16u (0x%x)",13 s, (unsigned int)pid, (unsigned int)ppid,14 (unsigned int)tid, (unsigned int)tid);15 }16 /*thread process*/
17 void *thread_func(void *arg);18 {19 printids("new thread:");20 return (void *)108;21 }22 /*main func*/
23 intmain()24 {25 interr;26 void *tret; /*thread return value*/
27 pthread_t ntid;28 err = pthread_create(&ntid, NULL, thread_func, NULL);29 if (err != 0)30 perror("can't create thread");31
32 err = pthread_join(ntid, &tret);33 if (err != 0)34 perror("can't join thread");35 printids("main thread:");36 printf("thread exit code: %d", (int)tret);37 sleep(1);38 return 0;39 }
pthread_cancel函数:
pthread_cancel函数发送终止信号
pthread_setcancelstate函数设置终止方式
pthread_testcancel函数取消线程(另一功能是:设置取消点)
1) 线程取消的定义
一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。
2) 线程取消的语义
线程取消的方法是向目标线程发Cancel信号(pthread_cancel函数发送Cancel信号),但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态(pthread_setcancelstate函数设置状态)决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
3 )取消点
根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、 pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用 pthread_testcancel(),从而达到POSIX标准所要求的目标,即如下代码段:
pthread_testcancel();
retcode = read(fd, buffer, length);
pthread_testcancel();
4 )程序设计方面的考虑
如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用。
5 )与线程取消相关的pthread函数
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。
int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和 PTHREAD_CANCEL_DISABLE,分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为 NULL则存入原来的Cancel状态以便恢复。
int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和 PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。
void pthread_testcancel(void)
功能一:设置取消点;
功能二:检查本线程是否处于Canceld状态,如果是,则进行取消动作,否则直接返回。
代码:
1 #include
2 #include
3 #include
4 #include
5 #include
6
7
8 #define THREAD_MAX 4
9
10
11 pthread_mutex_t mutex;12 pthread_t thread[THREAD_MAX];13
14
15 static inttries;16 static intstarted;17
18
19 void print_it(int *arg)20 {21 pthread_t tid;22 tid =pthread_self();23 printf("Thread %lx was canceled on its %d try.\n",tid,*arg);24 }25
26
27 void *Search_Num(intarg)28 {29 pthread_t tid;30 intnum;31 int k=0,h=0,j;32 intntries;33 tid =pthread_self();34
35 /*while(pthread_mutex_trylock(&mutex) == EBUSY)36 {37 printf("**************busy****************\n");38 pthread_testcancel();39 }*/
40 srand(arg);41 num = rand()&0xFFFFFF;42 //pthread_mutex_unlock(&mutex);
43
44 printf("thread num %lx\n",tid);45
46 ntries = 0;47 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);48 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);49
50 pthread_cleanup_push((void *)print_it,(void *)&ntries);51
52 while(1)53 {54 num = (num+1)&0xffffff;55 ntries++;56
57 if(arg ==num)58 {59 //只允许一个线程操作此处
60 while(pthread_mutex_trylock(&mutex) ==EBUSY) {61 //一个线程操作后其余线程进入次循环挂起,等待pthread_cancel函数发送cancel信号终止线程
62 k++;63 if(k == 10000)64 {65 printf("----------2busy2-----------\n");66 }67
68 pthread_testcancel();69 }70 tries =ntries;71 //pthread_mutex_unlock(&mutex);//如果加上这句话,将会有好几个线程找到主函数中设定的值pid
72 printf("Thread %lx found the number!\n",tid);73
74 for(j = 0;j
82 break;83 }84 if(ntries%100 == 0)85 {86 h++;87 /*线程阻塞,其他线程争夺资源,或者是等待pthread_cancel函数发送cancel信号终止线程*/
88 pthread_testcancel();89 /*这是为了弄明白pthread_testcancel函数的作用而设置的代码段*/
90 if(h == 10000)91 {92 h = 0;93 printf("----------thread num %lx-------------\n",tid);94 }95 }96 }97 pthread_cleanup_pop(0);98 return (void *)0;99 }100
101
102 intmain()103 {104 inti,pid;105
106 pid = getpid(); //设置要查找的数
107
108 pthread_mutex_init(&mutex,NULL);109 printf("Search the num of %d\n",pid);110 for(started = 0; started < THREAD_MAX; started++)111 {112 pthread_create(&thread[started],NULL,(void *)Search_Num,(void *)pid);113 }114
115 for(i = 0; i < THREAD_MAX; i++)116 {117 printf("-----------i = %d--------------\n",i);118 pthread_join(thread[i],NULL);119 }120 printf("It took %d tries ot find the number!\n",tries);121 return 0;122 }123 运行结果:124 Search the num of 6531
125 -----------i = 0--------------
126 thread num b6fbcb70127 thread num b67bbb70128 thread num b5fbab70129 thread num b77bdb70130 ----------thread num b67bbb70-------------
131 Thread b67bbb70 found the number!
132 ----------thread num b6fbcb70-------------
133 ----------thread num b77bdb70-------------
134 ----------2busy2-----------
135 ----------thread num b5fbab70-------------
136 ----------2busy2-----------
137 Thread b5fbab70 was canceled on its 1174527 try.138 Thread b77bdb70 was canceled on its 1023100 try.139 -----------i = 1--------------
140 Thread b6fbcb70 was canceled on its 1174527 try.141 -----------i = 2--------------
142 -----------i = 3--------------
143 It took 1174527 tries ot find the number!
void pthread_cleanup_push(void (*rtn)(void *),void *arg);
void pthread_cleanup_pop(int execute);
当线程执行以下动作时调用清理函数,调用参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。
1.调用pthread_exit时。2.响应取消请求时。3.用非零execute参数调用pthread_cleanup_pop时。
如果execute参数置为0,清理函数将不被调用。无论哪种情况,pthread_cleanup_pop都将删除上次pthread_clean_push调用建立的清理处理程序。
实例:
1 #include
2 #include
3 #include
4
5 void cleanup(void *arg)6 {7 printf("cleanup: %s\n", (char *)arg);8 }9
10 void *thr_fn1(void *arg)11 {12 printf("thread 1 start\n");13 pthread_cleanup_push(cleanup, "thread 1 first handler");14 pthread_cleanup_push(cleanup, "thread 1 second handler");15 printf("thread 1 push complete\n");16 if(arg)17 return((void *)1);18 //pthread_exit((void *)2);
19
20 pthread_cleanup_pop(0);21 pthread_cleanup_pop(0);22 //return((void *)1);
23 pthread_exit((void *)2);24
25 }26
27 void *thr_fn2(void *arg)28 {29 printf("thread 2 start\n");30 pthread_cleanup_push(cleanup, "thread 2 first handler");31 pthread_cleanup_push(cleanup, "thread 2 second handler");32 printf("thread 2 push complete\n");33 if(arg)34 pthread_exit((void *)2);35 pthread_cleanup_pop(0);36 pthread_cleanup_pop(0);37 pthread_exit((void *)2);38 }39
40 int main(void)41 {42 interr;43 pthread_t tid1, tid2;44 void *tret;45
46 err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);47 if (err != 0)48 printf("can't create thread 1: %c\n", strerror(err));49 err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);50 if (err != 0)51 printf("can't create thread 2: %c\n", strerror(err));52 err = pthread_join(tid1, &tret);53 if (err != 0)54 printf("can't join with thread 1: %c\n", strerror(err));55 printf("thread 1 exit code %d\n", (int)tret);56 err = pthread_join(tid2, &tret);57 if (err != 0)58 printf("can't join with thread 2: %c\n", strerror(err));59 printf("thread 2 exit code %d\n", (int)tret);60 exit(0);61 }62
pthread_detach()函数:
创建一个线程默认的状态是joinable。
如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码).
所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代 码,回收其资源(类似于wait,waitpid) 。
但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。
比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),这时可以
1)在子线程中加入代码pthread_detach(pthread_self())
2)父线程调用pthread_detach(thread_id)(非阻塞,可立即返回)
这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
pthread_kill()函数:
pthread_kill与kill有区别,是向线程发送signal。,大部分signal的默认动作是终止进程的运行,所以,我们才要用signal()去抓信号并加上处理函数。
int pthread_kill(pthread_t thread, int sig);
向指定ID的线程发送sig信号,如果线程代码内不做处理,则按照信号默认的行为影响整个进程,也就是说,如果你给一个线程发送了SIGQUIT,但线程却没有实现signal处理函数,则整个进程退出。
pthread_kill(threadid, SIGKILL)杀死整个进程。 如果要获得正确的行为,就需要在线程内实现signal(SIGKILL,sig_handler)。所以,如果int sig的参数不是0,那一定要清楚到底要干什么,而且一定要实现线程的信号处理函数,否则,就会影响整个进程。
如果int sig是0呢,这是一个保留信号,一个作用是用来判断线程是不是还活着。pthread_kill的返回值: 成功:0 线程不存在:ESRCH 信号不合法:EINVAL
代码:
int kill_rc = pthread_kill(thread_id,0);
if(kill_rc == ESRCH)
printf("the specified thread did not exists or already quit\n");
else if(kill_rc == EINVAL)
printf("signal is invalid\n");
else
printf("the specified thread is alive\n");
这里附上线程基本函数:
------------------------------------------------------------------------------------------
POSIX函数 描述
-------------------------------------------------------------------------------------------
pthread_create 创建一个线程
pthread_self 找出自己的线程ID
pthread_equal 测试2个线程ID是否相等
pthread_detach 设置线程以释放资源
pthread_join 等待一个线程
pthread_cancel 终止另一个线程
pthread_exit 退出线程,而不退出进程
pthread_kill 向线程发送一个信号
-------------------------------------------------------------------------------------------
线程属性pthread_attr_t简介
Posix线程中的线程属性pthread_attr_t主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。
pthread_attr_t的主要属性的意义如下:
__detachstate,表示新线程是否与进程中其他线程脱离同步, 如果设置为PTHREAD_CREATE_DETACHED 则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
__schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。
__schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。
__inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
__scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、pthread_attr_destroy()和与各个属性相关的pthread_attr_getXXX/pthread_attr_setXXX函数。
在设置线程属性 pthread_attr_t 之前,通常先调用pthread_attr_init来初始化,之后来调用相应的属性设置函数。
主要的函数如下:
1、pthread_attr_init
功能: 对线程属性变量的初始化。
头文件:
函数原型: int pthread_attr_init (pthread_attr_t* attr);
函数传入值:attr:线程属性。
函数返回值:成功: 0
失败: -1
2、pthread_attr_setscope
功能: 设置线程 __scope 属性。scope属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。默认为PTHREAD_SCOPE_PROCESS。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
头文件:
函数原型: int pthread_attr_setscope (pthread_attr_t* attr, int scope);
函数传入值:attr: 线程属性。
scope:PTHREAD_SCOPE_SYSTEM,表示与系统中所有线程一起竞争CPU时间,
PTHREAD_SCOPE_PROCESS,表示仅与同进程中的线程竞争CPU
函数返回值得:同1。
3、pthread_attr_setdetachstate
功能: 设置线程detachstate属性。该表示新线程是否与进程中其他线程脱离同步,如果设置为PTHREAD_CREATE_DETACHED则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
头文件:
函数原型: int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);
函数传入值:attr:线程属性。
detachstate:PTHREAD_CREATE_DETACHED,不能用pthread_join()来同步,且在退出时自行释放所占用的资源
PTHREAD_CREATE_JOINABLE,能用pthread_join()来同步
函数返回值得:同1。
4、pthread_attr_setschedparam
功能: 设置线程schedparam属性,即调用的优先级。
头文件:
函数原型: int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);
函数传入值:attr:线程属性。
param:线程优先级。一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0
函数返回值:同1。
5、pthread_attr_getschedparam
功能: 得到线程优先级。
头文件:
函数原型: int pthread_attr_getschedparam (pthread_attr_t* attr, struct sched_param* param);
函数传入值:attr:线程属性;
param:线程优先级;
函数返回值:同1。
例:
1 #include
2 #include
3 #include
4 #include
5 static void pthread_func_1 (void);6 static void pthread_func_2 (void);7
8 int main (int argc, char**argv)9 {10 pthread_t pt_1 = 0;11 pthread_t pt_2 = 0;12 pthread_attr_t atrr = {0};13 int ret = 0;14
15 /*初始化属性线程属性*/
16 pthread_attr_init (&attr);17 pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);18 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);19
20 ret = pthread_create (&pt_1, &attr, pthread_func_1, NULL);21 if (ret != 0)22 {23 perror ("pthread_1_create");24 }25
26 ret = pthread_create (&pt_2, NULL, pthread_func_2, NULL);27 if (ret != 0)28 {29 perror ("pthread_2_create");30 }31
32 pthread_join (pt_2, NULL);33
34 return 0;35 }36
37 static void pthread_func_1 (void)38 {39 int i = 0;40
41 for (; i < 6; i++)42 {43 printf ("This is pthread_1.\n");44
45 if (i == 2)46 {47 pthread_exit (0);48 }49 }50
51 return;52 }53
54 static void pthread_func_2 (void)55 {56 int i = 0;57
58 for (; i < 3; i ++)59 {60 printf ("This is pthread_2.\n");61 }62
63 return;64 }