Linux线程的调度策略
Linux系统中任务优先级分为两种:一种是静态优先级,一种是动态优先级,两者区别如下:
(1)静态优先级
静态优先级指的是任务一旦设置好优先级之后就不能再改变,相当于是任务本身的属性,通过Linux系统内核源码中的头文件sched.h可以知道,线程的 优先级范围是0~99,优先级数值越大则优先级越高。
Linux系统中提供了两个函数接口来设置以及获取线程的继承调度属性,分别是pthread_attr_setinheritsched()和pthread_attr_getinheritsched(),使用规则如下所示:

第一个参数:attr指的是线程的属性对象,用户在创建线程之前可以先设置线程的属性,其中线程的属性就包含线程的调度属性,线程的属性对象attr在调用pthread_attr_init()函数初始化之后,默认是继承创建该线程的线程的调度属性。
第二个参数:inheritsched指的是线程的属性是打算继承创建该线程的线程的调度属性或者指定为线程属性对象attr中的调度属性,如果该参数为PTHREAD_EXPLICIT_SCHED,则表示线程的调度属性指定为attr的值。
注意:一条线程如果以默认的属性进行创建,则该线程的调度策略被设置为SCHED_OTHER,并且线程的优先级默认设置为0,也就意味着创建的线程属于普通任务。
Linux系统中线程的调度策略分三种:SCHED_OTHER、SCHED_FIFO、SCHED_RR
A.SCHED_OTHER
SCHED_OTHER调度策略指的是分时调度策略,是Linux系统中默认的调度策略,一般采用CFS(Completely Fair Scheduler)算法,该算法为运行队列中的每一个进程都设置一个虚拟时钟vruntime(Virtual Runtime)。
如果一个进程得到执行,随着执行时间的不断增长,其vruntime也将不断增大,没有得到执行的进程vruntime将保持不变。而调度器将会选择最小的vruntime那个进程来执行。这就是所谓的“完全公平”。不同优先级的进程其vruntime增长速度不同,优先级高的进程vruntime增长得慢,所以可能会得到更多的运行机会。
注意:如果一个线程以SCHED_OTHER调度策略进行创建,线程的静态优先级必须设置为0。

B.SCHED_FIFO
SCHED_FIFO调度策略指的是实时调度策略,采用“先到先服务”的调度方式,当一条线程采用该调度策略则会一直运行,直到它被更高优先级的线程抢占或者主动放弃CPU,才会交出控制权。
当线程完成后,内核会去寻找处于就绪状态相同优先级的线程,如果不存在,则寻找低优先级线程。该调度策略实现了数据的互斥,在线程运行的时间内其他相同优先级线程无法进行资源抢占。
C.SCHED_RR
SCHED_RR(Round Robin,中文意为轮询)指的是实时调度策略,是一种基于时间片轮转的调度策略,它会给每个线程设置一个固定的优先级,并按照优先级顺序对线程进行轮流调度。
当一条线程采用该调度策略则会一直运行,直到它被更高优先级的线程抢占、主动放弃CPU以及消耗完自己的时间片,才会交出控制权。
时间片是线程运行的最小时间单元,由操作系统预先设定。当时间片用完时,该线程自动交出控制权,之后内核会按照和FIFO相同的方式搜索下一个工作线程。
轮转调度可以防止某一个任务连续占用太多的资源,而导致其他线程信息得不到及时处理。缺点是轮转调度会增大由于任务切换(任务上下文)而导致的开销。
Linux系统中线程创建时如果采用默认的属性,则线程的调度策略就是SCHED_OTHER,当然,Linux系统提供了一个名称叫做pthread_attr_setschedpolicy()的函数,用户可以利用该函数设置线程的调度属性。

注意:如果用户准备设置线程的调度属性,则应该先调用pthread_attr_setinheritsched()函数,并设置线程属性对象attr的继承调度属性为PTHREAD_EXPLICIT_SCHED。
创建一条指定属性的线程,要求线程的调度策略为先到先服务策略,提示:创建属性对象 --> 初始化属性对象 --> 设置属性对象的继承调度属性(不继承) --> 设置调度策略!
点击查看代码
#include <stdio.h>
#include <pthread.h>
pthread_attr_t attr;
//子线程1 调度策略:FIFO
void *task1(void *arg)
{
while(1)
{
printf("I am task1,is Running\n");
sleep(1);
}
}
//子线程2 调度策略:FIFO
void *task2(void *arg)
{
while(1)
{
printf("I am task2,is Running\n");
sleep(5);
}
}
//主线程 也算是普通任务
int main(int argc, char const *argv[])
{
struct sched_param param;
//1.对线程属性对象进行初始化
pthread_attr_init(&attr);
//2.初始化之后才可以获取线程属性对象中的继承调度属性
int inheritsched;
pthread_attr_getinheritsched(&attr,&inheritsched);
//表达式成立,说明默认的线程属性对象中的继承调度属性的值:继承
if (inheritsched == PTHREAD_INHERIT_SCHED)
{
printf("INHERIT\n");
}
else
{
printf("EXPLICIT\n");
}
//3.修改线程属性对象中的继承调度属性:不继承
pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
//4.修改线程属性对象的调度策略为:先到先服务 SCHED_FIFO
pthread_attr_setschedpolicy(&attr,SCHED_FIFO);
//5.修改创建的子线程的静态优先级
param.sched_priority = 1;
pthread_attr_setschedparam(&attr,¶m);
//6.创建子线程 子线程1的优先级是1
pthread_t thread1;
pthread_create(&thread1,&attr,task1,NULL);
//7.再次修改创建的子线程的静态优先级
param.sched_priority = 2;
pthread_attr_setschedparam(&attr,¶m);
//8.创建子线程 子线程1的优先级是2
pthread_t thread2;
pthread_create(&thread2,&attr,task2,NULL);
pthread_exit(NULL);
return 0;
}
(2)动态优先级
动态优先级指的是当多个普通任务并发运行时,系统会根据其实际运行的表现来动态地调整他们的nice值,任务的表示如下:
A.睡眠时间越多,放着系统资源不用,系统就倾向于判定其为IO消耗性的任务,会逐步提高其优先级
B.睡眠时间越少,拼命抢占系统资源,系统就倾向于判定其为CPU消耗性任务,会逐步降低其优先级
注意:Linux系统中线程的nice值越高,则优先级越低,而nice值越低,反而优先级越高!!!
用户可以修改普通任务的动态优先级,实际上就是修改普通任务的nice值即可,Linux系统提供了一个名字叫做nice()的函数接口,使用规则如下:

nice值的范围是-20 ~ 19,其中-20是最高优先级,19是最低优先级,所以nice值越小,则优先级越高。注意:进程的nice值默认为0,如果要降低nice值(即想要提高优先级),那么启动程序时必须加sudo,获得管理员的权限才能启动。
点击查看代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
//子线程1 调度策略:OTHER
void *task1(void *arg)
{
nice(19);
while(1)
{
printf("I am task1,is Running\n");
}
}
//子线程2 调度策略:OTHER
void *task2(void *arg)
{
nice(-20);
while(1)
{
printf("I am task2,is Running\n");
}
}
//主线程 也算是普通任务
int main(int argc, char const *argv[])
{
//1.创建子线程 普通任务
pthread_t thread1;
pthread_create(&thread1,NULL,task1,NULL);
//2.创建子线程
pthread_t thread2;
pthread_create(&thread2,NULL,task2,NULL);
pthread_exit(NULL);
return 0;
}
浙公网安备 33010602011771号