Linux下 线程优先级 和 线程调度策略 设置
线程的创建
线程创建的接口函数 pthread_create()
#include
int pthread_create(
pthread_t *restrict tidp, //新创建的线程ID指向的内存单元。
const pthread_attr_t *restrict attr, //线程属性,默认为NULL
void *(*start_rtn)(void *), //新创建的线程运行的函数
void *restrict arg //默认为NULL。如果线程运行的函数需要参数,将参数放入结构中并将地址作为arg传入。
);
线程属性结构体
线程属性的设置是根据实际的需要来设置的。默认属性下, 可以解决大部分开发时遇到的问题。
如果我们需要对线程提出更高的性能要求,就可以通过线程属性来设置 线程的优先级,线程的调度策略以及线程栈的大小等来满足。
下面是线程属性结构体的详解
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;
线程的分离状态
pthread_attr_setdetachstate / pthread_attr_getdetachstate
作用:设置/获取线程的分离状态。
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
参数:
attr : 线程属性结构体指针detachstate :
PTHREAD_CREATE_JOINABLE:可连接状态(默认)PTHREAD_CREATE_DETACHED:分离状态
分离状态(PTHREAD_CREATE_DETACHED)
系统在线程终止时自动回收其资源不能使用pthread_join()等待或获取线程返回值适用于
不需要知道线程何时结束或结果的场景简单的后台任务不需要与其他线程同步的任务
非分离状态(PTHREAD_CREATE_JOINABLE,默认):
线程终止后资源不会自动释放必须调用pthread_join()来回收资源并获取返回值适用于
需要获取线程执行结果的场景需要知道线程确切结束时间的场景需要线程间同步的场景
线程调度策略的设置
pthread_attr_setschedpolicy/ pthread_attr_getschedpolicy
作用:设置/获取线程的调度策略。
// policy 参数一般由3种 SCHED_OTHER, SCHED_FIFO, SCHED_RR.
int pthread_attr_setschedpolicy(pthread_attr_*, int policy)
//获取线程属性,将属性值赋给policy
int pthread_attr_getschedpolicy(const pthread_attr_t *, int * policy)
参数:
attr : 线程属性指针policy :
SCHED_FIFO:先进先出调度SCHED_RR : 时间片轮转SCHED_OTHER : 分时调度
SCHEAD_OTHER : 分时调度策略。线程默认调度策略,不区分优先级,该调度方式通过分时来完成的。
它旨在确保每个分时调度的线程都获得相同多的处理器时间。
请注意,这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。
例如:当实时线程准备就绪后,若当前运行的线程是分时线程,则实时线程会立即抢占分时线程。
SCHED_FIFO : 实时调度策略,先进先出原则,这种调度方式有优先级之分,并且无时间片概念。
于该调度策略时,高优先级的进程将会一直占用CPU 直到有更高优先级的线程出现,将线程设置为该调度策略的时候需要超级用户模式。当实时线程处于就绪状态时,当前使用cpu的线程优先级比它低时,会立即抢占。
SCHED_RR : 实时调度策略,时间片轮转。 在SCHED_FIFO的基础上加入了时间片。
当线程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。同样可以被高优先级抢占。
线程优先级的设置
pthread_attr_setschedparam/ pthread_attr_getschedparam
设置和获取线程优先级的接口函数主要通过 线程属性(pthread_attr_t) 和 调度参数(struct sched_param) 来实现
//线程优先级设置和获取函数
int pthread_attr_setschedparam(pthread_attr_t *,const struct sched_param *);
int pthread_attr_getschedparam(const pthread_attr_t *,struct sched_param *);
参数:
attr:线程属性结构体指针(需先 pthread_attr_init 初始化)param:调度参数结构体,其中 sched_priority 表示优先级
示例
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);
pthread_attr_setschedpolicy(&attr, SCHED_FIFO); // 设置调度策略(FIFO/RR/OTHER)
param.sched_priority = 50; // 设置优先级(1~99,取决于策略)
pthread_attr_setschedparam(&attr, ¶m);
pthread_t thread;
pthread_create(&thread, &attr, thread_func, NULL);
pthread_attr_destroy(&attr); // 销毁属性对象
优先级范围默认是1~99;具体范围可通过sched_get_priority_min()和sched_get_priority_max()获取
sched_priority 代表优先级,值越大优先级越高。
线程的继承性设置
pthread_attr_getinheritsched/ pthread_attr_setinheritsched
int pthread_attr_getinheritsched(const pthread_attr_t * attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t * attr,int inheritsched);
参数:
attr:线程属性结构体指针inheritsched
PTHREAD_INHERIT_SCHED:继承创建者的调度属性(默认)PTHREAD_EXPLICIT_SCHED:使用attr中显式设置的调度属性
继承调度属性(PTHREAD_INHERIT_SCHED):
新线程继承创建者线程的调度策略和参数忽略pthread_attr_t中设置的调度属性适用于:
需要保持线程组调度一致性的场景简单应用,不需要精细控制每个线程的调度
显式设置调度属性(PTHREAD_EXPLICIT_SCHED):
使用pthread_attr_t中显式设置的调度策略和参数不继承创建者线程的调度属性适用于:
需要为不同线程设置不同调度策略的场景实时应用需要精确控制线程优先级复杂的多线程调度需求
线程的栈大小设置
pthread_attr_setstacksize/ pthread_attr_getstacksize
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
参数:
attr:线程属性结构体指针stacksize:要设置/获取的栈大小(字节)
默认栈大小
Linux系统中,线程栈的默认大小通常为8MB(x86_64架构)可以通过ulimit -s命令查看系统默认栈大小
需要设置栈大小的情况
线程需要处理大量局部变量或深度递归调用时在内存受限的嵌入式系统中需要减少栈大小以节省内存需要创建大量线程时(减小栈大小可降低总内存消耗)
//例如:
{
int s;
pthread_attr_t attr;
struct sched_param param;
pthread_attr_init(&attr);//初始化线程属性
//获取线程的继承性 默认是 PTHREAD_INHERIT_SCHED
s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
s = pthread_attr_setschedpolicy(&attr, SCHED_RR);//设置调度模式
s = sched_get_priority_max(SCHED_RR); //获取RR调度模式下的最大线程优先级
param.sched_priority = s; //设置线程优先级
s = pthread_attr_setschedparam(&attr, ¶m); //将优先级添加到线程属性中
}
线程绑定CPU
//设置和获取当前线程绑定的CPU ID
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,const cpu_set_t *cpuset);
int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);
//从函数名以及参数名都很明了,唯一需要点解释下的可能就是cpu_set_t这个结构体了。这个结构体的理解类似于select中的fd_set,可以理解为cpu集,也是通过约定好的宏来进行清除、设置以及判断:
//初始化,设为空
void CPU_ZERO (cpu_set_t *set);
//将某个cpu加入cpu集中
void CPU_SET (int cpu, cpu_set_t *set);
//将某个cpu从cpu集中移出
void CPU_CLR (int cpu, cpu_set_t *set);
//判断某个cpu是否已在cpu集中设置了
int CPU_ISSET (int cpu, const cpu_set_t *set);
代码示例
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void *myThread1(void){
while(1)
{
printf("This is the 1th\n");
sleep(1);
}
}
void *myThread2(void){
while(1)
{
printf("This is the 2th pthread\n");
sleep(1);
}
}
void *myThread3(void)
{
struct timeval tv;
while(1){
gettimeofday(&tv,NULL);
printf("change priority befor millisecond:%ld\n",tv.tv_sec*1000 + tv.tv_usec/1000);
sleep(1);
}
}
void myThread4(void)
{
struct timeval tv;
while(1){
gettimeofday(&tv,NULL);
printf("change priority after millisecond:%ld\n",tv.tv_sec*1000 + tv.tv_usec/1000);
sleep(1);
}
}
void *thread_func(void *arg){
cpu_set_t cpuset;
int policy, ret;
struct sched_param param;
//获取线程调度参数
ret = pthread_getschedparam(pthread_self(), &policy, ¶m);
if(ret!=0){
printf("pthread_getschedparam %s\n", strerror(ret) );
exit(1);
}
if (policy == SCHED_FIFO){
printf("policy:SCHED_FIFO\n");
}
else if (policy == SCHED_OTHER){
printf("policy:SCHED_OTHER\n");
}
else if (policy == SCHED_RR){
printf("policy:SCHED_RR\n");
}
printf("thread_func priority is %d\n", param.sched_priority);
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset); /* cpu 1 is in cpuset now */
/* bind process to processor 1 */
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) !=0) {
perror("pthread_setaffinity_np");
}
printf("Core 1 is running! this pthread PID is %d\n",gettid());
myThread4();
}
int main(int argc, char *argv[]){
pthread_t my_thread;
time_t startwtime, endwtime;
pthread_t id1,id2,id3;
int ret = 0;
pthread_attr_t attr;
struct sched_param sp;
int policy ,inher;
int rs;
bzero((void*)&sp,sizeof(sp));
pid_t pid = gettid();//获取进程PID
pthread_t tid = pthread_self(); //获取当前线程ID
printf("main: pid=%d, tid=%lu\n", pid, tid);
/* 在默认情况下通过pthread_create函数创建的线程是非分离属性的,
* 由pthread_create函数的第二个参数决定,
* 在非分离的情况下,当一个线程结束的时候,
* 它所占用的系统资源并没有完全真正的释放,也没有真正终止。
* 只有在pthread_join函数返回时,该线程才会释放自己的资源。
* 或者是设置在分离属性的情况下,一个线程结束会立即释放它所占用的资源。
* pthread_join()函数会一直阻塞调用线程,直到指定的线程终止。
* 当pthread_join()返回之后,应用程序可回收与已终止线程关联的任何数据存储空间。
* 但是,同时需要注意,一定要和上面创建的某一线程配套使用,这样还可以起到互斥的作用。
* 否则多线程可能抢占CPU资源,导致运行结果不确定。
*/
rs = pthread_attr_init(&attr);
assert(rs == 0);
//获取继承的调度策略
ret = pthread_attr_getinheritsched(&attr, &inher);
if (ret!=0)
{
printf("pthread_attr_getinheritsched\n%s\n", strerror(ret));
exit(1);
}
//打印继承的调度策略
if (inher == PTHREAD_EXPLICIT_SCHED)
{
printf("PTHREAD_EXPLICIT_SCHED\n");
}
else if (inher == PTHREAD_INHERIT_SCHED)
{
printf("PTHREAD_INHERIT_SCHED\n");
inher = PTHREAD_EXPLICIT_SCHED;
}
//设置继承的调度策略
//必需设置inher的属性为 PTHREAD_EXPLICIT_SCHED,否则设置线程的优先级会被忽略
ret = pthread_attr_setinheritsched(&attr, inher);
if (ret!=0)
{
printf("pthread_attr_setinheritsched\n%s\n", strerror(ret));
exit(1);
}
policy = SCHED_RR; //需要超级用户权限
pthread_attr_setschedpolicy( &attr, policy );//设置 调度策略为SCHED_RR
assert( rs == 0 );
const int priority = 51; //设置优先级 为51
sp.__sched_priority = priority;
//if(0 == pthread_setschedparam(pthread_self(),policy,&sp)){
if(pthread_attr_setschedparam(&attr,&sp) != 0){//设置优先级
printf("pthread set sched priority failed\n");
}
if (pthread_create(&my_thread, &attr, thread_func,NULL) != 0) {
perror("pthread_create failed\n");
}
ret = pthread_create(&id1,NULL,(void*)myThread1,NULL);
if(ret){
printf("cread pthread1 failed\n ");
return -1;
}
ret = pthread_create(&id2,NULL,(void*)myThread2,NULL);
if(ret){
printf("cread pthread2 failed\n ");
return -1;
}
ret = pthread_create(&id3,NULL,(void*)myThread3,NULL);
if(ret){
printf("cread pthread3 failed\n ");
return -1;
}
pthread_join(my_thread,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
return 0;
}