Linux多线程实践(三)线程的基本属性设置API
POSIX 线程库定义了线程属性对象 pthread_attr_t ,它封装了线程的创建者能够訪问和改动的线程属性。主要包含例如以下属性:
1. 作用域(scope)
2. 栈尺寸(stack size)
3. 栈地址(stack address)
4. 优先级(priority)
5. 分离的状态(detached state)
6. 调度策略和參数(scheduling policy and parameters)
线程属性对象能够与一个线程或多个线程相关联。当使用线程属性对象时。它是对线程和线程组行为的配置。使用属性对象的全部线程都将具有由属性对象所定义的全部属 性。尽管它们共享属性对象。但它们维护各自独立的线程 ID 和寄存器。
初始化/销毁线程属性
int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr);线程分离属性
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);线程栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
普通情况下该值我们设置为0,使用系统默认设置的线程栈大小。否则可能会引起程序的可移植性的问题
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); int pthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize);
guardsize意思是假设我们使用线程栈超过了设定大小之后,系统还会使用部分扩展内存来防止栈溢出。而这部分扩展内存大小就是guardsize. 只是假设自己改动了栈分配位置的话,那么这个选项失效,效果相当于将guardsize设置为0.
每一个线程都存在自己的堆栈。假设这些堆栈是相连的话,訪问超过自己的堆栈的话那么可能会改动到其它线程的堆栈。 假设我们设置了guardsize的话,线程堆栈会多开辟guarszie大小的内存,当訪问到这块内存时会触发SIGSEGV信号。
线程竞争范围(进程范围内的竞争 or 系统范围内的竞争)int pthread_attr_getscope(const pthread_attr_t *attr,int *contentionscope); int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);
线程能够在两种竞争域内竞争资源:
1. 进程域(process scope):与同一进程内的其它线程
2. 系统域(system scope):与系统中的全部线程
作用域属性描写叙述特定线程将与哪些线程竞争资源。一个具有系统域的线程将与整个系 统中全部具有系统域的线程依照优先级竞争处理器资源,进行调度。
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
进程的调度策略和优先级属于主线程,换句话说就是设置进程的调度策略和优先级仅仅 会影响主线程的调度策略和优先级。而不会改变对等线程的调度策略和优先级(注这句话不全然正确)。
每一个对等线程能够拥有它自己的独立于主线程的调度策略和优先级。
在 Linux 系统中,进程有三种调度策略: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, int inheritsched);
在 pthread 库中,提供了一个函数,用来设置被创建的线程的调度属性:是从创建者线 程继承调度属性(调度策略和优先级),还是从属性对象设置调度属性。该函数就是:
int pthread_attr_setinheritsched (pthread_attr_t * attr, int inherit) 当中,inherit 的值为下列值中的其一: enum { PTHREAD_INHERIT_SCHED, //线程调度属性从创建者线程继承 PTHREAD_EXPLICIT_SCHED //线程调度属性设置为 attr 设置的属性 };
假设在创建新的线程时,调用该函数将參数设置为 PTHREAD_INHERIT_SCHED 时,那么当改动进程的优先级时。该进程中继承这个优先级而且还没有改变其优先级的所 有线程也将会跟着改变优先级(也就是刚才那句话部分正确的原因)。
线程调度參数(实际上我们一般仅仅关心一个參数:线程的优先级,默觉得0)
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); //sched_param结构体 struct sched_param { int sched_priority; /* Scheduling priority */ };线程的并发级别
int pthread_setconcurrency(int new_level); int pthread_getconcurrency(void);
说明:并发级别仅在N:M线程模型中有效。设置并发级别,给内核一个提示:表示提供给定级别数量的核心线程来映射用户线程是高效的(不过一个提示),默觉得0, 内核依照默认的方式进行并发。
我们来写一个样例来运用:
/** 查看线程默认属性 **/ void printThreadAttr() { pthread_attr_t attr; pthread_attr_init(&attr); int detachstate; pthread_attr_getdetachstate(&attr, &detachstate); cout << "detach-state: " << (detachstate == PTHREAD_CREATE_JOINABLE ?"PTHREAD_CREATE_JOINABLE" : "PTHREAD_CREATE_DETACHED") << endl; size_t size; pthread_attr_getstacksize(&attr, &size); cout << "stack-size: " << size << endl; pthread_attr_getguardsize(&attr, &size); cout << "guard-size: " << size << endl; int scope; pthread_attr_getscope(&attr, &scope); cout << "scope: " << (scope == PTHREAD_SCOPE_SYSTEM ? "PTHREAD_SCOPE_SYSTEM" : "PTHREAD_SCOPE_PROCESS") << endl; int policy; pthread_attr_getschedpolicy(&attr, &policy); cout << "policy: "; switch (policy) { case SCHED_FIFO: cout << "SCHED_FIFO"; break; case SCHED_RR: cout << "SCHED_RR"; break; case SCHED_OTHER: cout << "SCHED_OTHER"; break; default: break; } cout << endl; int inheritsched; pthread_attr_getinheritsched(&attr, &inheritsched); cout << "inheritsched: " << (inheritsched == PTHREAD_INHERIT_SCHED ?
"PTHREAD_INHERIT_SCHED" : "PTHREAD_INHERIT_SCHED") << endl; struct sched_param param; pthread_attr_getschedparam(&attr, ¶m); cout << "scheduling priority: " << param.sched_priority << endl; cout << "concurrency: " << pthread_getconcurrency() << endl; pthread_attr_destroy(&attr); }
说明:
绑定属性:
Linux中採用“一对一”的线程机制。也就是一个用户线程相应一个内核线程。
绑定属性就是指一个用户线程固定地分配给一个内核线程。由于CPU时间片的调度是面向内核线程(也就是轻量级进程)的,因此具有绑定属性的线程能够保证在须要的时候总有一个内核线程与之相应。而与之相应的非绑定属性就是指用户线程和内核线程的关系不是始终固定的,而是由系统来控制分配的。
分离属性:
分离属性是用来决定一个线程以什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它所占用的系统资源并没有被释放。也就是没有真正的终止。
仅仅有当pthread_join()函数返回时,创建的线程才干释放自己占用的系统资源。而在分离属性情况下,一个线程结束时马上释放它所占有的系统资源。这里要注意的一点是,假设设置一个线程的分离属性。而这个线程执行又非常快,那么它非常可能在pthread_create()函数返回之前就终止了。它终止以后就可能将线程号和系统资源移交给其它的线程使用。