2. 多线程并发打印

任务目标

在 Zephyr RTOS 中创建 3 个不同优先级的线程,各自循环打印独立消息,通过串口终端观察线程调度顺序与时间片轮转效果。本案例聚焦于线程创建、优先级配置与调度行为验证。

环境依赖

  • Zephyr RTOS 3.x(以 main 分支为例)
  • 开发板:qemu_x86(无需真实硬件,便于快速验证)
  • west 工作流已配置完成

完整实现

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* 线程栈与线程控制块 */
#define STACK_SIZE 512
#define THREAD_NUM  3

K_THREAD_STACK_DEFINE(thread_stack_0, STACK_SIZE);
K_THREAD_STACK_DEFINE(thread_stack_1, STACK_SIZE);
K_THREAD_STACK_DEFINE(thread_stack_2, STACK_SIZE);

struct k_thread thread_data_0;
struct k_thread thread_data_1;
struct k_thread thread_data_2;

/* 线程入口函数 */
void thread_entry(void *p1, void *p2, void *p3)
{
    const char *msg = (const char *)p1;
    int id = (int)(intptr_t)p2;

    while (1) {
        printk("[Thread %d] %s (priority: %d)\n",
               id, msg, k_thread_priority_get(k_current_get()));
        k_msleep(100);  /* 主动让出 CPU,便于观察调度 */
    }
}

void main(void)
{
    printk("Main: starting multi-thread demo...\n");

    /* 创建 3 个线程,优先级分别为 2、3、4(数值越小优先级越高) */
    k_thread_create(&thread_data_0, thread_stack_0, STACK_SIZE,
                    thread_entry, (void *)"Hello from T0", (void *)0, NULL,
                    2, 0, K_NO_WAIT);
    k_thread_create(&thread_data_1, thread_stack_1, STACK_SIZE,
                    thread_entry, (void *)"Hello from T1", (void *)1, NULL,
                    3, 0, K_NO_WAIT);
    k_thread_create(&thread_data_2, thread_stack_2, STACK_SIZE,
                    thread_entry, (void *)"Hello from T2", (void *)2, NULL,
                    4, 0, K_NO_WAIT);

    /* 主线程退出,让调度器运行上述线程 */
}

构建与运行

# 在项目根目录下执行
west build -b qemu_x86 -p always
west build -t run

验证方法与判定标准

  1. 观察串口输出:终端应持续打印 [Thread 0]、[Thread 1]、[Thread 2] 三种消息,每条消息间隔约 100ms。
  2. 调度顺序验证:由于线程 0 优先级最高(数值 2 最小),在无其他阻塞时它应最先被调度;随后线程 1、线程 2 依次获得 CPU。由于每个线程执行 k_msleep(100) 主动让出,实际输出会呈现近似轮转的交替顺序,而非严格按优先级连续执行。
  3. 优先级抢占验证:若将线程 0 的 k_msleep 改为 k_yield(),可观察到高优先级线程 0 会持续抢占 CPU,线程 1 和 2 几乎无法运行——这验证了优先级抢占调度机制。
  4. 通过条件:终端能稳定看到 3 个线程的交替打印,且每条消息中的优先级数值与创建时配置一致(2、3、4),即视为通过。

预期输出(节选)

Main: starting multi-thread demo...
[Thread 0] Hello from T0 (priority: 2)
[Thread 1] Hello from T1 (priority: 3)
[Thread 2] Hello from T2 (priority: 4)
[Thread 0] Hello from T0 (priority: 2)
[Thread 1] Hello from T1 (priority: 3)
...

关键说明

  • K_NO_WAIT 表示线程创建后立即进入就绪队列,无需延迟启动。
  • 线程入口函数的 p2 参数通过整数转换传递线程 ID,用于打印区分。
  • 若需观察时间片轮转(同优先级线程),可将 3 个线程设为相同优先级(如均为 4),并移除 k_msleep,此时 CONFIG_TIMESLICE_SIZE 决定每个线程的运行时长。