3、任务优先级与调度策略:抢占式调度、时间片轮转、合作式调度

调度策略,说白了就是操作系统决定「下一个该谁跑」的规则。我做了这么多年嵌入式,见过太多因为调度策略选错而翻车的项目。今天咱们就把这三种主流策略掰开揉碎讲清楚。

3.1 任务优先级——调度的核心依据

每个任务都有一个优先级数值。数值越小,优先级越高——这是RTOS里的惯例。嗯,也有反着来的,比如FreeRTOS就是数值越大优先级越高,但道理一样。

我在项目中遇到过一件事:有个同事把两个关键任务的优先级设成了相同值,结果系统跑起来后,一个任务死活抢不到CPU。为什么?因为时间片轮转没开,同优先级任务之间不会主动让出CPU。你看,优先级设计看似简单,坑却不少。

优先级设计的基本原则:

  • 实时性要求高的任务,给高优先级
  • 计算量大的任务,给低优先级
  • 中断服务相关的任务,通常给最高优先级
  • 空闲任务永远给最低优先级

3.2 抢占式调度——谁急谁先跑

抢占式调度,是RTOS最常用的策略。它的规则很简单:高优先级任务就绪后,立刻打断当前正在运行的低优先级任务。

你想想看,这就像急诊室。来了个危重病人,医生马上放下手头的普通门诊去抢救。等抢救完了,再回来继续看普通病人。

// 抢占式调度示例
void TaskA(void *param) {
    while(1) {
        // 优先级=5,中等优先级
        printf("TaskA running\n");
        vTaskDelay(100);  // 主动阻塞,让出CPU
    }
}

void TaskB(void *param) {
    while(1) {
        // 优先级=10,高优先级
        printf("TaskB running\n");
        // 注意:这里没有阻塞!
        // TaskB会一直运行,TaskA永远得不到CPU
    }
}

我曾经踩过的坑:高优先级任务如果一直不阻塞,低优先级任务会饿死。所以高优先级任务里一定要有阻塞点——比如等待信号量、消息队列,或者主动延时。

3.3 时间片轮转——公平是王道

时间片轮转,说白了就是「一人跑一会儿」。同优先级的任务,每个任务分到一个固定时间片,时间到了就切换到下一个。

我个人习惯在UI任务和后台计算任务上用时间片轮转。为什么?因为UI不能卡死,后台计算也不能太慢。两者优先级相同,时间片轮转,谁也别想独占CPU。

调度策略 适用场景 优点 缺点
抢占式 实时性要求高的系统 响应快,高优先级任务及时执行 低优先级任务可能饿死
时间片轮转 同优先级任务较多 公平,每个任务都有机会 上下文切换开销大
合作式 资源受限的简单系统 无锁,无竞态 一个任务卡死,全系统瘫痪

3.4 合作式调度——自觉才是美德

合作式调度,也叫非抢占式调度。任务自己决定什么时候让出CPU。你不让?那其他任务就等着。

我记得早期做8位单片机项目时,资源少得可怜,连个像样的RTOS都跑不起来。那时候就用合作式调度——每个任务里手动调用task_yield()来让出CPU。

// 合作式调度示例
void Task1(void) {
    while(1) {
        // 做一部分工作
        do_some_work();
        // 主动让出CPU
        task_yield();
        
        // 再做一部分工作
        do_another_work();
        task_yield();
    }
}

void Task2(void) {
    while(1) {
        // 如果Task1不调用task_yield()
        // Task2永远得不到执行
        process_data();
        task_yield();
    }
}

我的建议:合作式调度适合任务数量少、每个任务执行时间短且可控的场景。比如简单的传感器采集、LED闪烁控制。千万别在复杂系统里用合作式——一个任务出bug死循环,整个系统就挂了。

3.5 三种策略的混合使用

实际项目中,很少只用一种策略。大多数RTOS都支持混合调度。比如FreeRTOS,默认是抢占式,但同优先级任务之间可以用时间片轮转。

我做过一个物联网网关项目,里面用了三种策略的混合:

  • 中断处理任务:抢占式,最高优先级
  • 网络协议栈任务:抢占式,高优先级
  • UI刷新和日志记录:时间片轮转,中等优先级
  • 后台数据统计:合作式,最低优先级

你看,不同任务对实时性的要求不同,调度策略也要「因材施教」。

3.6 调度策略选择指南

到底该用哪种?我总结了几条经验:

  1. 有硬实时任务(比如电机控制、音频处理)→ 必须用抢占式
  2. 同优先级任务多(比如多个UI界面)→ 开启时间片轮转
  3. 资源极度受限(比如4KB RAM的MCU)→ 考虑合作式
  4. 任务之间有数据共享 → 抢占式要加锁,合作式不用

核心原则:调度策略没有银弹。选型时要综合考虑实时性、资源开销、开发复杂度。我见过有人用抢占式调度做LED闪烁,结果因为优先级反转导致灯闪得乱七八糟——杀鸡用牛刀,反而出问题。

3.7 知识体系图

下面这张图展示了三种调度策略的核心逻辑和适用场景:

任务调度策略知识体系 调度策略 抢占式调度 时间片轮转 合作式调度 高优先级抢占 实时响应快 需处理竞态 公平分配CPU 同优先级轮转 切换开销大 主动让出CPU 无需锁机制 任务卡死风险 适用场景 硬实时任务、中断响应 UI任务、后台计算 资源受限、简单系统 混合使用:抢占式 + 时间片轮转 + 合作式

这张图把三种策略的核心特性、适用场景以及混合使用的关系都画出来了。你可以把它当作选型时的参考地图。


可可精品资料库