Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
ART-Pi Smart
POSIX
【ART-PI Smart 抛砖引玉 七】基于POSIX的应用开发实践之任务队列
发布于 2022-05-16 22:03:51 浏览:597
订阅该版
[tocm] ## 任务队列的引入 在应用程序的设计中,要执行的任务有可能会存在多个,并且多个待处理的任务在短时间内连续出现,此时可以针对每一个任务请求实时创建对应的线程来进行响应处理。但是,实际应用场景下,短时间内频繁的线程创建和删除带来的系统开销有时候将不可忽视,此时在线程数有限或者线程数远小于当前要处理任务的情况下,就需要一种机制来对系统中当前要处理的任务进行管理。其中最简单最常用的方式就是当前待处理的任务进行排队,构成一个类似任务FIFO的机制,通过某种管理机制,实现从该顺序队列中获取要执行的任务,然后分配给相关的线程来执行。 本文基于上述考的应用情形,设计了针对任务的队列机制,其中包括单个任务数据结构的建立,还有任务队列对象的数据抽象设计,以及队列后续操作的相关行为函数接口,还有临界资源的保护等方面。下面给出任务队列具体的设计流程。因为只是简单的任务队列,所以只实现基本的通用操作。尽量实现模块的设计,方便后续应用的快速调用。 ## 任务对象设计 在任务队列中,每一个队列成员对应一个单独的任务数据结构,任务数据结构即程序中要执行任务抽象,它由将要执行的任务函数指针和对应的要传入的参数指针构成。 ```c typedef struct Task { void (*func)(void* arg); void* arg; }task_t; ``` ## 任务队列对象设计 任务队列对象由两部分组成,一部分用来对任务队列自身特性进行描述,另一部分用来对队列操作的行为进行描述。下面分别给出任务对象设计的两部分构成细则。 ### 队列的参数构成 | 队列参数 | 功能描述 | | ------------- | -------------------------------------------- | | tasks | 存放整个队列的内存指针 | | Cap | 记录队列容量 | | Head | 当前队列头所在的位置,用于从该位置中获取任务 | | Tail | 当前队列尾所在的位置,向该位置中写入新的任务 | | currentTakCnt | 当前队列中任务的个数 | | mutex | 队列参数操作保护锁 | ### 队列的行为 队列的行为主要是针对队列在实际操作过程中要实现的具体功能进行了接口的封装,通过函数指针统一由任务队列对象来初始化和管理,间接的把任务队列的参数保护起来,通过功能接口来调用。 | 队列对象行为 | 功能描述 | | ------------------ | ------------------ | | *addTask | 向队列添加任务 | | *getTask | 从队列获取任务 | | *getCurrentTaskNum | 获取当前队列任务数 | | *getQueueCap | 获取队列容量 | | *getQueueHead | 获取队列头位置 | | *getQueueTail | 获取队列尾位置 | | *destroy | 销毁队列 | ### 任务队列对象的数据结构 ```C typedef struct taskqueue { task_t* tasks; int Cap; int Head; int Tail; int currentTakCnt; pthread_mutex_t mutex; int (*addTask)(void* this, void (*func)(void*), void* arg); task_t (*getTask)(void* this); int (*getCurrentTaskNum)(void* this); int (*getQueueCap)(void* this); int (*getQueueHead)(void* this); int (*getQueueTail)(void* this); int (*destroy)(void* this); }taskQueue_t; ``` ## 任务队列接口设计 | API接口 | 功能描述 | | :--------------- | ------------ | | taskQueue_create | 创建任务队列 | 任务队列主要的API接口为任务队列创建函数,该函数会逐一创建出任务队列对象的实例,并且返回动态创建的对象内存块指针。在任务队列使用完毕,调用任务队列对象行为函数即可销毁当前任务队列对象实例。 ```c taskQueue_t* taskQueue_create(int queueCap) { taskQueue_t* queue = (taskQueue_t*) malloc(sizeof(taskQueue_t)); do { if(queue == NULL) { printf("[error ] create queue error!\n"); break; } queue->tasks = (task_t*)malloc(sizeof(task_t)*queueCap); if(queue->tasks == NULL) { printf("[error ] create tasks error!\n"); break; } queue->Cap = queueCap; queue->Head = 0; queue->Tail = 0; queue->currentTakCnt = 0; pthread_mutex_init(&queue->mutex, NULL); queue->addTask = taskQueue_addTaskOnTail; queue->getTask = taskQueue_getTaskOnHead; queue->getCurrentTaskNum = taskQueue_getCurrentTaskNum; queue->getQueueCap = taskQueue_getQueueCap; queue->getQueueHead = taskQueue_getQueueHead; queue->getQueueTail = taskQueue_getQueueTail; queue->destroy = taskQueue_destroy; return queue; }while(0); if(queue && queue->tasks) { free(queue->tasks); } if(queue) { free(queue); } return NULL; } ``` ## 任务队列行为函数设计 ### 销毁任务队列 对应于任务队列的创建,任务队列的销毁包含在了任务队列的行为函数中,当不需要再使用队列时,通过任务队列实例调用该接口函数即可实现自我销毁,释放占用的系统资源。 ```C static int taskQueue_destroy(void* this) { taskQueue_t* queue = (taskQueue_t*) this; if(queue == NULL) { printf("[error ] taskQueue_destroy: input parameter NULL\n"); return -1; } queue->Cap = 0; queue->Head = 0; queue->Tail = 0; queue->currentTakCnt = 0; pthread_mutex_destroy(&queue->mutex); free(queue->tasks); queue->tasks=NULL; free(queue); queue=NULL; return 0; } ``` ### 向任务队列中添加任务 任务队列对象成员tasks用来承载添加进来的各种任务,在添加任务时,采用了在队列尾(写入位置变量来标记)添加,初始化时队列成员变量tail为0,表示队列为空,每次添加一个任务到队列,tail变量自动后移到下一个位置为下一次添加任务做指示。当任务添加满时,下一次自动从队列头开始添加,构成一个循环操作。在对队列成员变量进行操作时,队列锁将会保护其操作的原子性。如果向队列中添加任务时,队列中的任务成员已经达到了队列的最大容量,则会主动返回任务添加失败。 ```c static int taskQueue_addTaskOnTail(void* this, void (*func)(void*), void* arg) { taskQueue_t* queue = (taskQueue_t*) this; pthread_mutex_lock(&queue->mutex); if(queue->currentTakCnt == queue->Cap) { printf("[info] tasks already full!\n"); return -1; } queue->tasks[queue->Tail].func = func; queue->tasks[queue->Tail].arg = arg; queue->Tail = (queue->Tail+1) % queue->Cap; queue->currentTakCnt++; pthread_mutex_unlock(&queue->mutex); return 0; } ``` ### 从任务队列中取出任务 从任务队列中取出任务的流程和添加任务到队列中的顺序相反,从队列头取出数据,队列对象通过head成员变量来指示当前可以取出任务的位置,任务对象自身的互斥锁成员来保护当前任务队列对象的相关操作,取出head位置的任务,并且更新对应的任务队列参数,并返回取出的任务数据。head位置随着任务的取出,自动循环移动。 ```c static task_t taskQueue_getTaskOnHead(void* this) { taskQueue_t* queue = (taskQueue_t*) this; task_t task; if(queue == NULL) { printf("[error ] taskQueue_getCurrentTaskNum: input parameter NULL\n"); task.func = NULL; task.arg = NULL; return task; } pthread_mutex_lock(&queue->mutex); task.func = queue->tasks[queue->Head].func; task.arg = queue->tasks[queue->Head].arg; queue->Head = (queue->Head+1) % queue->Cap; queue->currentTakCnt--; pthread_mutex_unlock(&queue->mutex); return task; } ``` ### 获取任务队列状态的参数 获取任务队列的状态和参数主要是通过一系列的相关的接口函数来对外返回相关的数据,例如获取队列当前的任务数,主要是通过如下接口函数来实现,主要是返回队列对应的成员数据。在获取任务队列状态和参数的过程中,因为只是读取和返回队列的成员数据,不涉及修改,因此此类操作不需要利用任务互斥锁来进行保护。 ```c static int taskQueue_getCurrentTaskNum(void* this) { taskQueue_t* queue = (taskQueue_t*) this; if(queue == NULL) { printf("[error ] taskQueue_getCurrentTaskNum"); return -1; } int num = queue->currentTakCnt; return num; } ``` 获取任务队列其他状态和参数的流程和上述操作类似。在此不做赘述。 ## 总结 本设计简单的对任务队列进行了抽象,建立了任务队列的数据结构,并且通过函数指针接口,实现了任务队列的一系列操作行为,从而对外隐藏了任务队列属性参数,所有操作都通过对外接口进行统一访问。实现了任务队列的模块化设计,对后续应用场合提供了模块化的支持。 本篇的结尾,照例是诗分享。希望这片隐藏在技术贴最后的小小空间,能够成为你我生活中某天的意外焰火。 > “这是个旅途 一个叫做命运的茫茫旅途 我们偶然相遇 然后离去 在这条永远不归的路 我们路过高山 我们路过湖泊 我们路过森林 路过沙漠 路过人们的城堡和花园 路过幸福 我们路过痛苦 路过一个女人的温暖和眼泪 路过生命中漫无止境的寒冷和孤独” ————朴树《旅途》
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
RickFlying
something in the way
文章
10
回答
45
被采纳
0
关注TA
发私信
相关文章
1
移植rt-thread2.1.0缺少components/pthreads里的posix_types.h文件中包含的sy
2
[征集自愿者] POSIX相关章节文档编写
3
RT-Thread POSIX支持
4
POSIX标准接口针对文件系统没有导出fsync
5
POSIX接口的select还没实现?
6
pthread 组建bug反馈
7
RTT中的POSIX支持
8
RT-Thread 如何用POSIX接口
9
使用 RT_USING_POSIX finsh能显示,但不能输入
10
请教、讨论POSIX接口、dfs中的pos和size问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
9
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部