目录
RT-Thread的线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。
通过2个进程了解调度
static void thread1Entry(void *parameter)
{
uint32_t count = 0;
while (1)
{
Printf("1:%d\n", count++);
delayms(600);
}
}
static void thread2Entry(void *parameter)
{
uint32_t count = 0;
while (1)
{
Printf("2:%d\n", count++);
delayms(300);
}
}
为了减少打印信息,加入delayms函数延时(通过for循环延时)。
1.优先级调度
将2个进程创建为不同的优先级,进程1的优先级高于进程2。
rtosCreateThread(
handle1,
"thread1",
thread1Entry,
(void *)0,
NULL,
256,
RT_THREAD_PRIORITY_MAX - 2,
10);
if(handle1 != NULL)
Printf("Create Thread 1 OK\n");
rtosCreateThread(
handle2,
"thread2",
thread2Entry,
(void *)0,
NULL,
256,
RT_THREAD_PRIORITY_MAX - 1,
10);
if(handle2 != NULL)
Printf("Create Thread 2 OK\n");
rtosStartThread(handle1);
rtosStartThread(handle2);
从打印结果看,进程1一直在运行,进程2则没有运行过。
如果把2个进程的优先级设置为一样的(注意2个进程的时间片大小设置成一样的)。由于进程1延时时间是进程2的一倍,所以看到的打印结果是:
1:0
2:0
2:1
1:1
2:2
2:3
1:2
2:4
2:5
这里碰到一个问题,程序运行一段时间后,进程2就独占CPU了,进程1不再运行,GD32F450会很快发现这个问题,而STM32则需要很久才会发生。
2. 时间片调度
相同优先级线程按照时间片轮番调度。
将上面例子中2个进程的时间片修改为300,600.
1:0
2:0
2:1
2:2
1:1
2:3
2:4
2:5
2:6
2:7
进程1分配了300ms,延时600ms,所以开始运行了一次后交出控制权,进程2分配了600ms,延时300ms,所以这里会打印多次‘2’(delayms并不是很准导致不是2次)。
从这可以看出去,分配时间片长的相当于优先级更高。
3. 主动切换
进程可以主动交出CPU控制权,最常见的方法是进程睡眠。
3.1 进程睡眠
将上例中的2个进程设置为不同优先级(同章节1),然后把进程1的delayms改为RTOS的睡眠函数rtosThreadSleep。
static void thread1Entry(void *parameter)
{
uint32_t count = 0;
while (1)
{
Printf("1:%d\n", count++);
rtosThreadSleep(600 * RTOS_TICK_PER_SECOND / 1000);
}
}
打印结果:
1:0
2:0
2:1
2:2
1:1
2:3
2:4
1:2
2:5
2:6
2:7
进程1先运行1次,睡眠后交出控制权,进程2开始运行,因为此时进程1是睡眠600ms,进程1的优先级比进程2高,所以进程2一直运行直到进程1的睡眠时间到,马上就拿到控制权。
3.2 放弃进程
通过rt_thread_yield放弃进程应该仅对优先级相同时有效,在上面3.1的例子上将rtosThreadSleep改为rt_thread_yield();
测试发现一直运行进程1。这是因为进程在执行完rt_thread_yield后进入就绪状态,但是进程1的优先级比进程2高,所以还是执行进程1。
将进程1的优先级改为和进程2一样,并且将时间片改为1200,而进程2的时间片仍然为600
rtosCreateThread(
handle1,
"thread1",
thread1Entry,
(void *)0,
NULL,
256,
RT_THREAD_PRIORITY_MAX - 1,
1200);
打印结果:
1:0
2:0
2:1
2:2
1:1
2:3
2:4
1:2
2:5
2:6
2:7
尽管进程1分配了2倍的时间片,但是运行yield后直接交出了控制权,进程2运行分配的600ms时间片后进程1再执行一次。