RT-Thread workqueue 詳解
在學(xué)習(xí)之前可以先去了解一下工作隊(duì)列的使用場(chǎng)景:工作隊(duì)列 ( workqueue )绷落。
簡(jiǎn)而言之悬秉,工作隊(duì)列就是將一些工作任務(wù)的執(zhí)行延遲卷哩,交由內(nèi)核線程異步執(zhí)行领突。
如何使用
最簡(jiǎn)單的使用方式就是開啟 RT-Thread 的系統(tǒng)工作線程(System workqueue)踩娘,而我們往系統(tǒng)工作線程里提交工作項(xiàng)(work item)即可瞪慧。
RT-Thread 其實(shí)給我們提供了一個(gè)系統(tǒng)工作線程了压恒,但很少有人知道从祝。配置選項(xiàng)路徑如下圖所示:
RT-Thread Components
-> Device Drivers
-> Using device drivers IPC
-> Using system default workqueue
依次選中上述這些選項(xiàng),就能夠開啟系統(tǒng)工作隊(duì)列了垂谢。而且還可以看到工作隊(duì)列線程的棧大小默認(rèn)為 2048厦画,優(yōu)先級(jí)為 23 。
這樣系統(tǒng)在初始化的時(shí)候就創(chuàng)建了系統(tǒng)工作隊(duì)列了滥朱,名字叫作 sys_work
根暑,在終端輸入 ps
能夠看到該線程。
如何向系統(tǒng)工作線程里添加工作項(xiàng)呢徙邻?
/**
* @brief Submit a work to system workqueue.
* @param work A work item.
* @param time Action delay time.
* @return RT_EOK on success, Ohters on fail.
*/
rt_err_t rt_work_submit(struct rt_work *work, rt_tick_t time);
/**
* @brief Cancel a work in system workqueue.
* @param work A work item.
* @return RT_EOK on success, Others on fail.
*/
rt_err_t rt_work_cancel(struct rt_work *work);
rt_work_submit()
用于向系統(tǒng)工作隊(duì)列添加工作項(xiàng)排嫌,rt_work_cancel()
用于從系統(tǒng)工作隊(duì)列中取消某一個(gè)工作項(xiàng)。
當(dāng)然缰犁,在提交工作項(xiàng)時(shí)躏率,需要初始化該工作項(xiàng),綁定相應(yīng)的回調(diào)函數(shù)和用戶指針民鼓,接口如下:
/**
* @brief Init a work item.
* @param work_func A callback function.
* @param work_data User data pass to `work_func`.
* @return None.
*/
rt_inline void rt_work_init(struct rt_work *work, void (*work_func)(struct rt_work *work, void *work_data), void *work_data)
這樣薇芝,我們就可以隨時(shí)隨地地提交工作任務(wù)執(zhí)行了,極大地方便了程序的組織丰嘉。
用一個(gè)小例程測(cè)試一下:
在 qemu 項(xiàng)目里的 main.c 里輸入:
#include <stdio.h>
#include <dlfcn.h>
#include <rtthread.h>
#include <ipc/workqueue.h>
struct rt_work work1;
int work1_data = 1;
struct rt_work work2;
int work2_data = 2;
void work_func(struct rt_work *work, void *work_data)
{
int data = *(int *)work_data;
rt_kprintf("recv work data: %d\n", data);
}
int main(void)
{
printf("hello rt-thread!\n");
rt_work_init(&work1, work_func, &work1_data);
rt_work_init(&work2, work_func, &work2_data);
rt_work_submit(&work1, 2);
rt_work_submit(&work2, 0);
return 0;
}
然后執(zhí)行就能看到下述效果夯到,與工作項(xiàng)綁定的任務(wù)被異步執(zhí)行了,而且工作項(xiàng) 1 延遲了 2 個(gè) tick 才執(zhí)行饮亏。
rt_workqueue
rt_workqueue 的接口有很多耍贾,我們只需要關(guān)注常用的即可。
/**
* @brief Create a workqueue.
* @param name Workqueue thread name.
* @param stack_size Workqueue thread stack size.
* @param priority Workqueu thread priority.
* @return Workqueue handle.
*/
struct rt_workqueue *rt_workqueue_create(const char *name, rt_uint16_t stack_size, rt_uint8_t priority);
/**
* @brief Destory a workqueue.
* @param queue Workqueue handle.
* @return RT_EOK on success, Others on fail.
*/
rt_err_t rt_workqueue_destroy(struct rt_workqueue *queue);
/**
* @brief Submit a work item to workqueue.
* @param queue Workqueue handle.
* @param work Work item.
* @param time Action delay time.
* @return RT_EOK on success, Others on fail.
*
*/
rt_err_t rt_workqueue_submit_work(struct rt_workqueue *queue, struct rt_work *work, rt_tick_t time);
/**
* @brief Cancel a work item in workqueue.
* @param queue Workqueue handle.
* @param work Work item.
* @return RT_EOK on success, Others on fail.
*
*/
rt_err_t rt_workqueue_cancel_work(struct rt_workqueue *queue, struct rt_work *work);
首先使用 rt_workqueue_create()
創(chuàng)建一個(gè)工作隊(duì)列路幸,然后使用 rt_workqueue_submit_work()
提交工作項(xiàng)荐开,使用 rt_workqueue_cancel_work()
取消工作項(xiàng),當(dāng)然還可以使用 rt_workqueue_destroy()
銷毀一個(gè)工作隊(duì)列简肴。其他的接口有興趣的可以了解晃听,但常用的就是上面這四種。
這里提交任務(wù)與上述使用系統(tǒng)工作隊(duì)列的唯一不同之處就是我們需要手動(dòng)指定工作隊(duì)列砰识,其他的都是一模一樣的能扒。
用一個(gè)小例程測(cè)試一下:
在 qemu 項(xiàng)目里的 main.c 里輸入:
#include <stdio.h>
#include <dlfcn.h>
#include <rtthread.h>
#include <ipc/workqueue.h>
struct rt_work work1;
int work1_data = 1;
struct rt_work work2;
int work2_data = 2;
void work_func(struct rt_work *work, void *work_data)
{
int data = *(int *)work_data;
rt_kprintf("recv work data: %d\n", data);
}
int main(void)
{
printf("hello rt-thread!\n");
struct rt_workqueue *wq = rt_workqueue_create("my_wq", 2048, 20);
RT_ASSERT(wq);
rt_work_init(&work1, work_func, &work1_data);
rt_work_init(&work2, work_func, &work2_data);
rt_workqueue_submit_work(wq, &work1, 2);
rt_workqueue_submit_work(wq, &work2, 0);
return 0;
}
然后執(zhí)行就能看到下述效果,與工作項(xiàng)綁定的任務(wù)被異步執(zhí)行了辫狼,而且工作項(xiàng) 1 延遲了 2 個(gè) tick 才執(zhí)行初斑。
實(shí)現(xiàn)
關(guān)于實(shí)現(xiàn)部分我這里不介紹具體細(xì)節(jié),做了一些動(dòng)畫給大家展示一下內(nèi)部過(guò)程
工作隊(duì)列里面有一個(gè)線程(workthread)膨处,這個(gè)線程的任務(wù)就是不斷地從掛載鏈表(worklist)里提取工作項(xiàng)執(zhí)行见秤,若沒有則休眠砂竖。
然后提交工作項(xiàng)時(shí),若延遲時(shí)間 time 大于 0鹃答,則啟動(dòng)該工作項(xiàng)的定時(shí)器晦溪,定時(shí)結(jié)束后再加入掛載鏈表(worklist)。
若提交工作項(xiàng)時(shí)延遲實(shí)際等于 0挣跋,則直接將該工作項(xiàng)掛加入到掛載鏈表(worklist)。
當(dāng)然狞换,工作項(xiàng)的定時(shí)器超時(shí)后避咆,會(huì)自動(dòng)將該工作項(xiàng)加入到掛載鏈表(worklist)。