线程调度
在 Linux 中,调度器是基于线程的调度策略(scheduling policy)和静态调度优先级(static scheduling priority)来决定哪个线程来运行。
对于以下三种调度策略:SCHED_OTHER, SCHED_IDLE, SCHED_BATCH,其调度优先级是不起作用的,即可以将调度优先级视为 0;调度策略 SCHED_FIFO 和 SCHED_RR 是实时策略,其调度值范围是 1 到 99,数值越大优先级越高。下面介绍几种常见的调度策略:
- SCHED_OTHER:该策略是分时调度(time-sharing scheduling)策略,它是 Linux 线程默认的调度策略。SCHED_OTHER 策略的静态优先级总是为 0,对于该策略列表上的线程,调度器是基于动态优先级(dynamic priority)来调度的。动态优先级跟 nice 值相关,该值会随着线程的运行时间而动态改变,以确保所有具有 SCHED_OTHER 策略的线程公平运行。在 Linux 上,nice 值的范围是 -20 到 +19,默认值为0;nice值越大则优先级越低。
- SCHED_FIFO:先入先出调度策略(First in-first out scheduling)。该策略就是一旦线程抢占到 cpu 则一直运行,直到被更高优先级的线程抢占或或自己主动放弃。
- SCHED_RR:时间片轮转调度 (Round-robin scheduling)。该策略是 SCHED_FIFO 基础上改进来的,增加了一个时间片限制。当时间片用完后,系统将把该线程置于队列末尾。放在队列尾保证了所有具有相同优先级的 RR 任务的调度公平。
相关接口
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destory(pthread_attr_t *attr);
线程属性对象的初始化和消耗。
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);
获取指定调度策略可以设置的最大优先级和最小优先级。
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
设置线程的调度策略。参数 policy 可以是 SCHED_FIFO, SCHED_RR 和 SCHED_OTHER。系统创建线程时,默认的线程调度策略是 SCHED_OTHER。
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
获取线程的调度策略。
struct sched_param {
int sched_priority; /* Scheduling priority */
};
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
设置线程的调度优先级。
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
获取线程调度的优先级。
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
设置线程继承权。线程优先级不是随便就能设置的,需要放弃线程的继承权。第二个参数 inheritsched 有两个取值:PTHREAD_INHERIT_SCHED(拥有继承权)和 PTHREAD_EXPLICIT_SCHED(放弃继承权)。默认为 PTHREAD_INHERIT_SCHED。
int sched_setaffinity(pid_t pid, size_t cpusetsize, const cpu_set_t *mask);
int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize,
const cpu_set_t *cpuset);
int pthread_attr_setaffinity_np(pthread_attr_t *attr,
size_t cpusetsize, const cpu_set_t *cpuset);
以上两组接口都是设置线程的 CPU 亲和性。设置线程的亲和性可以使得线程绑定到一个或多个指定的 CPU 上运行。在多处理器系统上,设置 CPU 亲和性可以提高性能(主要原因是尽可能避免了cache 失效和切换到其他 CPU 的消耗)。
实例测试
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
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, priority = %d\n", threadno, param.sched_priority);
break;
case SCHED_RR:
printf("SCHDE_RR %d, priority = %d\n", threadno, param.sched_priority);
break;
case SCHED_FIFO:
printf("SCHED_FIFO %d, priority = %d\n", threadno, param.sched_priority);
break;
default:
printf("UNKNOWN\n");
}
}
void* run( 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 < 10; ++i) {
for(j = 0; j < 100000000; ++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[6];
pthread_t pth[6];
struct sched_param param;
for(i = 0; i < 6; ++i) {
pthread_attr_init(&attr[i]);
if (i < 2) {
param.sched_priority = 40;
pthread_attr_setschedpolicy(&attr[i], SCHED_FIFO);
pthread_attr_setschedparam(&attr[i], ¶m);
pthread_attr_setinheritsched(&attr[i], PTHREAD_EXPLICIT_SCHED);
}
if(i < 4 && i >= 2) {
param.sched_priority = 80;
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 < 6; ++i) {
if (i < 4) {
pthread_create(&pth[i], &attr[i], run, (void*)i);
} else {
pthread_create(&pth[i], NULL, run, (void*)i);
}
}
for(i = 0; i < 6; ++i)
pthread_join(pth[i], NULL);
for(i = 0; i < 6; ++i)
pthread_attr_destroy(&attr[i]);
return 0;
}
运行结果:
thread 0 start
thread 1 start
thread 2 start
thread 3 start
thread 5 start
thread 4 start
SCHED_FIFO 0, priority = 40
SCHED_FIFO 1, priority = 40
SCHED_FIFO 2, priority = 80
SCHED_FIFO 3, priority = 80
SCHED_OTHER 4, priority = 0
SCHED_OTHER 5, priority = 0
thread 3
thread 0
thread 4
thread 2
thread 1
thread 5
thread 0
thread 4
thread 1
thread 3
thread 5
thread 2
thread 4
thread 1
thread 5
thread 0
thread 3
thread 2
thread 4
thread 1
thread 5
thread 3
thread 4
thread 2
thread 1
thread 5
thread 0
thread 3
thread 4
thread 2
thread 1
thread 5
thread 3
thread 1
thread 4
thread 2
thread 0
thread 3
thread 1
thread 2
thread 5
thread 3
thread 4
thread 1
thread 2
thread 0
thread 5
thread 3
thread 1
thread 1 exit
thread 2
thread 4
thread 0
thread 3
thread 3 exit
thread 5
thread 2
thread 2 exit
thread 4
thread 4 exit
thread 0
thread 5
thread 5 exit
thread 0
thread 0
thread 0 exit