工作队列

本页依据 文档基线与后续更新 中记录的 Zephyr main 快照更新。

工作队列由一个线程按队列顺序执行回调,适合把 ISR 中耗时但可延后的工作转移到线程上下文。 它也适合多个轻量任务共享线程栈。

工作项的生命周期

struct k_work 表示工作项,回调签名是 void handler(struct k_work *work)。 初始化必须发生在首次提交之前。系统队列可使用 k_work_submit();自定义队列使用 k_work_submit_to_queue()。

同一个工作项反复提交不等于为每次事件保存了一条消息。已经排队的工作可能合并提交, 回调也可能与提交者并发执行。需要记录每次输入时,应把数据保存在消息队列、FIFO 或受保护 的状态中,再让工作项处理它们。

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

static void report_handler(struct k_work *work)
{
    ARG_UNUSED(work);
    printk("deferred work\n");
}

K_WORK_DEFINE(report_work, report_handler);

int request_report(void)
{
    return k_work_submit(&report_work);
}

工作项及回调访问的数据必须在排队和执行期间一直有效。 不能把函数内的临时 struct k_work 提交后立即返回,也不能在工作未结束时释放容器对象。

延迟工作

使用 struct k_work_delayable 和 K_WORK_DELAYABLE_DEFINE,而不是早期的延迟工作类型。

static void delayed_handler(struct k_work *work)
{
    ARG_UNUSED(work);
    printk("delay elapsed\n");
}

K_WORK_DELAYABLE_DEFINE(delayed_work, delayed_handler);

int request_debounced_report(void)
{
    return k_work_reschedule(&delayed_work, K_MSEC(100));
}

k_work_schedule() 在尚未安排工作时设置期限;已有安排时通常保持原期限。 k_work_reschedule() 更新期限,适合从最近一次输入重新计算去抖延时。 期限到达意味着工作可进入队列,不保证回调在该时刻立即开始,队列前方的工作和线程优先级 都会影响实际执行时间。

取消、等待与竞争

k_work_cancel()、k_work_cancel_delayable() 发起取消,但回调可能已经运行。 需要确定回调完全停止再释放对象时,在线程上下文使用对应的同步取消接口, 如 k_work_cancel_delayable_sync(),并提供在调用期间有效的 struct k_work_sync。

k_work_flush() 用于等待某项已提交工作完成,k_work_queue_drain() 面向整个队列。 不要在同一队列的处理线程中等待只有该线程才能执行完的其他工作。 k_work_is_pending() 等状态查询只是瞬时观察,不能用“先检查、再提交/释放”代替同步。 用户态线程、同步辅助对象的存储和 API 权限还应遵循对应接口约束。

自定义队列

如果回调可能阻塞很久,或者需要独立优先级,可创建自己的队列:

K_THREAD_STACK_DEFINE(worker_stack, 1024);
static struct k_work_q worker_queue;

void start_worker(void)
{
    k_work_queue_start(&worker_queue, worker_stack,
                       K_THREAD_STACK_SIZEOF(worker_stack), 5, NULL);
}

调用一次 start_worker() 后,通过 k_work_submit_to_queue(&worker_queue, &report_work) 提交。1024 字节和优先级 5 只是示例,需按目标架构、回调调用链和实时性要求调整。 系统工作队列的栈、优先级和是否在工作项间 yield 可由对应 Kconfig 控制; 避免依赖旧教程中的固定默认值。

上游依据