我们在进行多线程编程的时间,通常先会对问题领域进行任务的拆解,深入一点的多线程编程,会涉及到任务优先级的考虑;如果再深一点,一般可能就是多核编程:Cache热度、绑核、隔离CPU等。
但多核编程与具体硬件绑定较为紧密,通用性比较差,不到万不得已的时候,多核编程实为下策,应避免用之!
在此,要介绍的是在多线程编程中调度策略编程
。相较于多核编程,可能更贴合普通的使用场景!
对于某一些任务类型,例如,负载均衡器LB,这类任务有相当的密集CPU使用需求,但又不是100%的CPU消耗。在普通Linux CFS公平调度策略下,任务在时间片到时需要被换下马重新调度,在需要执行时,又因为历史上CPU使用时间的“被动”,经过动态计算优先级,又不一定能够马上被调度起来,可能会造成某些程度的丢包!
但在Linux系统分为实时系统和非实时系统,以前因为认知上的偏差,对于Linux CFS公平调度策略小小有点不满,为什么不可以实现线程调度策略定制的自由呢?
确实,Linux是自由的!近来,通过查看man 7 sched
,让自己满足地惊艳了一把,原来Linux早就实现了这种定制需求!
多核编程不见得效果更好,建议使用通用性更好的实时调度策略编程
在Linux中调度策略分为实时调度策略和非实时策略。
深入分析了下SCHED_RR
策略,此种调度策略已经可以比较好地满足实时性要求高和CPU使用密集的负载平衡器的多线程任务场景。
直白一点说就是:对于CPU较为密集使用的场景,想在自己退让CPU使用时,才脱离CPU执行;而当想使用CPU时,又可以获得较为实时的调度,上马执行
SCHED_RR
实时调度策略,在调度等级上高于普通调度策略;同时实时任务优先级为静态优先级,不进行动态调整
通过控制内核最长使用CPU时间片时间
/proc/sys/kernel/sched_rr_timeslice_ms
,可以以控制任务在需要退让CPU使用时,才脱离CPU使用通过查看
/proc/{pid}/status
中voluntary_ctxt_switches、nonvoluntary_ctxt_switches
两个统计项,可以观察任务主动让出CPU和被动让出CPU的比值,以量化策略控制的效果
在适合的多线程场景,建议多线程编程定制合适的调度策略
#include
#include
#include
#include
#include
#include
#include
#include
#define __USE_GNU
#include
#include
static int g_exit;
static void term_handler(int s)
{
g_exit = 1;
}
/*
* cat /proc/sys/kernel/sched_rt_runtime_us
* cat /proc/sys/kernel/sched_rr_timeslice_ms
* cat /sys/fs/cgroup/cpu/user.slice/cpu.rt_runtime_us
* echo $((10*1000)) > /sys/fs/cgroup/cpu/user.slice/cpu.rt_runtime_us
*/
int main(int argc , char* argv[])
{
char szErrorDesc[256];
struct sigaction act, oldact;
act.sa_handler = term_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, &oldact);
sigaction(SIGTERM, &act, &oldact);
//signal(SIGINT, SIG_IGN);
pid_t pid = getpid();
printf("pid: %d, policy: %d\n", pid, sched_getscheduler(pid));
int policy = -1;
int opt;
while((opt = getopt(argc, argv, "rb")) != -1) {
switch(opt) {
case 'r':
policy = SCHED_RR;
break;
case 'b':
policy = SCHED_BATCH;
break;
default: /* '?' */
fprintf(stderr, "Usage: %s [-r --SCHED_RR] [-b --SCHED_BATCH] \n", argv[0]);
return 1;
}
}
if(policy != -1) {
struct sched_param tParam = {};
if(policy == SCHED_RR)
tParam.sched_priority = 1;
int rc = sched_setscheduler(pid, policy, &tParam);
if(rc < 0)
{
strerror_r(errno, szErrorDesc, sizeof(szErrorDesc));
fprintf(stderr, "sched_setscheduler fail, errno: %d, %s", errno, szErrorDesc);
}
else
{
printf("after set thread policy: %d\n", sched_getscheduler(pid));
}
}
char szCmd[64];
snprintf(szCmd, sizeof(szCmd), "cat /proc/%d/status", pid);
printf("can see %s to see the context switch\n", szCmd);
uint64_t count = 0;
while(g_exit == 0) {
++count;
}
printf("cat %s to see the context switch result\n", szCmd);
system(szCmd);
return 0;
}