任务通知理论和使用

文章目录

  • 一、任务通知是什么?
    • 1.1任务通知的优势
    • 1.2任务通知的限制
    • 1.3通知状态和通知值
  • 二、任务通知的使用
    • 2.1任务通知使用_轻量级信号量
    • 2.2任务通知使用_轻量级队列
    • 2.3任务通知使用_轻量级事件组


一、任务通知是什么?

我们使用队列、信号量、事件组等等方法时,并不知道对方是谁。使用任务通知时,可以明确指定:通知哪个任务。
使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结构体通信。
使用任务通知时,任务结构体TCB中就包含了内部对象,可以直接接收别人发过来的"通知"。

1.1任务通知的优势

效率高: 使用任务通知来发送事件、数据给某个任务时,效率更高。比队列、信号量、事件组都
有大的优势。
省内存: 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。启用任务通知功能的开销固定为每个任务8个字节的RAM

1.2任务通知的限制

  1. 不能发送数据给ISR: ISR并没有任务结构体,所以无法使用任务通知的功能给ISR发送数据。但是ISR可以使用任务通知的功能,发数据给任务。
  2. 数据只能给该任务独享: 使用队列、信号量、事件组时,数据保存在这些结构体中,其他任务、ISR都可以访问这些数据。使用任务通知时,数据存放入目标任务中,只有它可以访问这些数据。在日常工作中,这个限制影响不大。因为很多场合是从多个数据源把数据发给某个任务,而不是把一个数据源的数据发给多个任务。
  3. 无法缓冲数据: 使用队列时,假设队列深度为N,那么它可以保持N个数据。使用任务通知时,任务结构体中只有一个任务通知值,只能保持一个数据。
  4. 无法广播给多个任务: 事件组可以同时给多个任务发送事件。使用任务通知,只能发个一个任务。
  5. 发送方无法进入阻塞状态: 接收方无法接收数据,发送方也无法阻塞等待,只能即刻返回错误。

1.3通知状态和通知值

每个任务都有一个结构体TCB(Task Control Block),里面有2个成员:
一个是uint8_t类型,用来表示通知状态
一个是uint32_t类型,用来表示通知值

通知状态ulNotifiedValue有3种取值:

状态说明
taskNOT_WAITING_NOTIFICATION任务没有在等待通知
taskWAITING_NOTIFICATION任务在等待通知
taskNOTIFICATION_RECEIVED任务接收到了通知,也被称为pending(有数据了,待处理)

通知值可以有很多种类型:计数值、位(类似事件组)、任意数值

二、任务通知的使用

使用任务通知,可以实现轻量级的队列(长度为1)、邮箱(覆盖的队列)、(计数型/二进制)信号量事件组

发送函数作用接收函数作用
xTaskNotifyGiveval++ulTaskNotifyTakeval-- 或 val = 0
xTaskNotifyxTaskNotifyWait
不使用val,只起通知作用可以在函数进入时清除val的某些位
可以在函数退出前清除val的某些位
可以取得val的值
val |= (bits)
val++
val = xxx
不覆盖,
当ucNotifyState表示在等待才起效
val = xxx
覆盖

2.1任务通知使用_轻量级信号量

在任务中使用xTaskNotifyGive函数,在ISR中使用vTaskNotifyGiveFromISR函数,都是直接给其他任务发送通知:

  1. 使得通知值加一
  2. 使得通知状态变为"pending",也就是 taskNOTIFICATION_RECEIVED,表示有数据了、待处理可以使用ulTaskNotifyTake函数来取出通知值:
  3. 如果通知值等于0,则阻塞(可以指定超时时间)
  4. 当通知值大于0时,任务从阻塞态进入就绪态
  5. 在ulTaskNotifyTake返回之前,还可以做些清理工作:把通知值减一,或者把通知值清零

信号量任务通知信号量对比如下:

信号量使用任务通知实现信号量
创建SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount,
UBaseType_t uxInitialCount );
GivexSemaphoreGive( SemaphoreHandle_t xSemaphore );BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
TakexSemaphoreTake(
SemaphoreHandle_t xSemaphore,
TickType_t xBlockTime
);
uint32_t ulTaskNotifyTake(
BaseType_t xClearCountOnExit,
TickType_t xTicksToWait
);

xTaskNotifyGive函数的参数说明如下:

参数说明
xTaskToNotify任务句柄(创建任务时获得),表示要发送通知的任务
返回值必定返回pdPASS,表示操作成功

ulTaskNotifyTake函数的参数说明如下:

参数说明
xClearCountOnExit函数返回前是否清零通知值。pdTRUE表示清零,pdFALSE表示如果通知值大于0,则减一
xTicksToWait任务进入阻塞态的超时时间,等待通知值大于0。0表示立即返回,portMAX_DELAY表示一直等待直到通知值大于0,其他值表示以Tick Count的方式等待
返回值函数返回之前,在清零或减一之前的通知值。如果xTicksToWait非0,有两种情况:1. 大于0,表示在超时前通知值被增加了;2. 等于0,表示一直没有其他任务增加通知值,最后超时返回0

实验1: 任务1发送数据通知,任务2接收

void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000; i++)sum++;//printf("1");for (i = 0; i < 10; i++){// xSemaphoreGive(xSemCalc);xTaskNotifyGive(xHandleTask2);}vTaskDelete(NULL);}
}void Task2Function(void * param)
{int i = 0;int val;while (1){//if (flagCalcEnd)flagCalcEnd = 0;//xSemaphoreTake(xSemCalc, portMAX_DELAY);val = ulTaskNotifyTake(pdFALSE, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d, NotifyVal = %d, i = %d\r\n", sum, val, i++);}
}

pdFALSE,表示通知值大于0,则减一。所以可以接收到所有通知。结果如下:

在这里插入图片描述

pdTRUE表示通知值清零,所以只能接收一次。结果如下:

在这里插入图片描述

2.2任务通知使用_轻量级队列

因为TCB结构体value只能存一个数据,所以任务通知实现队列可以只能容纳一个32位数据,且写队列不可阻塞,数据可覆盖也可不覆盖。正常队列可以容纳多个数据,且数据大小可指定,写队列可以阻塞。

队列与使用任务通知实现队列对比如下:

队列使用任务通知实现队列
创建QueueHandle_t xQueueCreate(
UBaseType_t uxQueueLength,
UBaseType_t uxItemSize
);
发送BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
BaseType_t xTaskNotify(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction
);
接收BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait
);
BaseType_t xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait
);

xTaskNotify函数的参数说明如下:

参数说明
xTaskToNotify任务句柄(创建任务时得到),表示要给哪个任务发通知
ulValue通知值,如何使用取决于eAction参数
eAction操作类型,具体含义见下表
返回值pdPASS表示成功,大部分调用都会成功;pdFAIL表示失败,仅当eAction为eSetValueWithoutOverwrite且通知状态为"pending"时会失败
eNotifyAction取值说明
eNoAction仅仅是更新通知状态为"pending",未使用ulValue。
这个选项相当于轻量级的、更高效的二进制信号量。
eSetBits通知值 = 原来的通知值 | ulValue,按位或。
相当于轻量级的、更高效的事件组。
eIncrement通知值 = 原来的通知值 + 1,未使用ulValue。
相当于轻量级的、更高效的二进制信号量、计数型信号量。
相当于xTaskNotifyGive()函数。
eSetValueWithoutOverwrite不覆盖。
如果通知状态为"pending"(表示有数据未读),
则此次调用xTaskNotify不做任何事,返回pdFAIL。
如果通知状态不是"pending"(表示没有新数据),
则:通知值 = ulValue。
eSetValueWithOverwrite覆盖。
无论如何,不管通知状态是否为"pendng",
通知值 = ulValue。

xTaskNotifyWait函数的参数说明如下:

参数说明
ulBitsToClearOnEntry在进入xTaskNotifyWait时要清除通知值的哪些位。只有在通知状态不是"pending"时才会清除。清除的方式是通知值 = 通知值 & ~(ulBitsToClearOnEntry)。
ulBitsToClearOnExit在退出xTaskNotifyWait时,如果是因为接收到了数据而退出(而不是超时),要清除通知值的哪些位。清除的方式是通知值 = 通知值 & ~(ulBitsToClearOnExit)。在清除之前,通知值会被赋给pulNotificationValue。
pulNotificationValue用于接收通知值。在函数退出时,使用ulBitsToClearOnExit清除通知值之前,通知值会被赋给pulNotificationValue。如果不需要接收通知值,可以将其设为NULL。
xTicksToWait任务进入阻塞态的超时时间,等待通知状态变为"pending"。0表示立即返回,portMAX_DELAY表示一直等待直到通知状态变为"pending",其他值表示以Tick Count的方式等待。
返回值pdPASS表示成功,表示xTaskNotifyWait成功获得了通知。pdFAIL表示失败,表示没有得到通知。

实验2: 任务1发送,任务2接收

void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000; i++)sum++;//printf("1");//flagCalcEnd = 1;//vTaskDelete(NULL);for (i = 0; i < 10; i++){//xQueueSend(xQueueCalcHandle, &sum, portMAX_DELAY);//xTaskNotify(xHandleTask2, sum, eSetValueWithoutOverwrite);xTaskNotify(xHandleTask2, sum, eSetValueWithOverwrite);sum++;}vTaskDelete(NULL);//sum = 1;}
}void Task2Function(void * param)
{int val;int i = 0;while (1){//if (flagCalcEnd)flagCalcEnd = 0;//xQueueReceive(xQueueCalcHandle, &val, portMAX_DELAY);xTaskNotifyWait(0, 0, &val, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d, i = %d\r\n", val, i++);}
}

eSetValueWithoutOverwrite不覆盖 结果

在这里插入图片描述

eSetValueWithOverwrite覆盖 结果

2.3任务通知使用_轻量级事件组

使用任务通知实现轻量级的事件组,我们将eAction配置为 eSetBits,通知值 = 原来的通知值 | ulValue。并且只要调用xTaskNotify就会唤醒目标任务,这与事件组 只有设置某一位或某几位满足条件才会唤醒任务 不符合。所以在 目标任务需要做一些判断才能模拟事件组。

实验3: 任务1、任务2发出事件,任务3等待事件。任务1设置bit0,任务2设置bit1,任务3等待这两个都发生才退出

void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 100000; i++)sum++;xQueueSend(xQueueCalcHandle, &sum, 0);/* 设置事件0 *///xEventGroupSetBits(xEventGroupCalc, (1<<0));xTaskNotify(xHandleTask3, (1<<0), eSetBits);printf("Task 1 set bit 0\r\n");vTaskDelete(NULL);}
}void Task2Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 1000000; i++)dec--;xQueueSend(xQueueCalcHandle, &dec, 0);/* 设置事件1 *///xEventGroupSetBits(xEventGroupCalc, (1<<1));xTaskNotify(xHandleTask3, (1<<1), eSetBits);printf("Task 2 set bit 1\r\n");vTaskDelete(NULL);}
}void Task3Function(void * param)
{int val1, val2;int bits;while (1){/*等待事件  *///xEventGroupWaitBits(xEventGroupCalc, (1<<0)|(1<<1), pdTRUE, pdTRUE, portMAX_DELAY);xTaskNotifyWait(0, 0, &bits, portMAX_DELAY);if ((bits & 0x3) == 0x3){vTaskDelay(20);xQueueReceive(xQueueCalcHandle, &val1, 0);xQueueReceive(xQueueCalcHandle, &val2, 0);printf("val1 = %d, val2 = %d\r\n", val1, val2);}else{vTaskDelay(20);printf("have not get all bits, get only 0x%x\r\n", bits);}}
}

结果: 任务2循环时间是任务1十倍,所以任务1将bit0置1,任务3响应后发现bits & 0x3仍未0,直到任务2将bit1置1,任务3打印出val1val2

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/11570.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅析视频汇聚EasyCVR视频融合云平台在机场安防智能检测建设中的应用

一、背景 机场作为国家交通枢纽和对外开放的窗口&#xff0c;其安全运行直接关系到乘客的生命安全、国家形象以及社会经济稳定。随着全球航空业的快速发展和人们出行需求的持续增长&#xff0c;机场作为重要的交通枢纽&#xff0c;其客流量和货运量均呈现出快速增长的态势。然而…

HCIP的学习(16)

BGP的状态机 ​ OSPF的状态机是在描述整个协议的完整工作过程&#xff0c;而BGP的状态机仅描述的是对等体关系建立过程中的状态变化。-----因为BGP将邻居建立过程以及BGP路由收发过程完全隔离。 ​ IGP协议在启动后&#xff0c;需要通过network命令激活接口&#xff0c;从而使…

SD-WAN供应商选择指南

企业网络日益演变&#xff0c;尤其是跨国企业、出海电商和外贸企业&#xff0c;其网络需求变得愈发复杂多样。SD-WAN技术因此备受瞩目&#xff0c;成为连接分支机构和数据中心的关键解决方案。然而&#xff0c;市面上的SD-WAN供应商众多&#xff0c;如何选择适合的服务商成为了…

gitignore配置不生效记录

第一种可能性&#xff1a; 在你所有的文件都通过了git add . 命令的情况下&#xff0c;使用指令git rm -r --cached .进行缓存清除&#xff0c;完成之后&#xff0c;再次通过git add . 然后通过git status去看提交的文件都有哪些。 第二种可能性 如果上面的不行就是你添加的…

【机器学习】Scikit-Learn:Python机器学习的瑞士军刀

Scikit-Learn&#xff1a;Python机器学习的瑞士军刀 一、Scikit-Learn简介二、Scikit-Learn的核心功能数据预处理模型选择模型评估模型部署 三、Scikit-Learn的中文社区与API四、代码实例&#xff1a;使用Scikit-Learn进行鸢尾花数据集分类 在当今这个数据驱动的时代&#xff0…

论文《Deep graph tensor learning for temporal link prediction》阅读

论文《Deep graph tensor learning for temporal link prediction》阅读 论文概况IntroductionRelated work动态图表示学习图张量表示 Preliminary张量生成建模 深度图张量学习模型A.基于图紧凑的空间表示B.时间模式表示C.时空特征聚合D.损失函数 实验数据集对比实验消融实验参…

鸿蒙ArkUI开发:常用布局【交叉轴】

交叉轴 垂直于主轴方向的轴线。Row容器交叉轴为纵向&#xff0c;Column容器交叉轴为横向。通过alignItems属性设置子元素在交叉轴&#xff08;排列方向的垂直方向&#xff09;上的对齐方式alignSelf属性用于控制单个子元素在容器交叉轴上的对齐方式&#xff0c;其优先级高于al…

M 有效算法

M 有效算法 本题考验二分知识&#xff0c;思路是二分k的取值&#xff0c;就按第一组样例来说当我们k取值为1的时候我们遍历数组想让|8-x|<k1的话x的取值范围是7-9&#xff0c;想让|3-x|<k2的话x的取值范围是1-5&#xff0c;两者x的区间不重合&#xff0c;说明肯定没有x能…

BGP—边界网关协议

BGP 动态路由协议可以按照工作范围分为IGP以及EGP。IGP工作在同一个AS内&#xff0c;主要用来发现和计算路由&#xff0c;为AS内提供路由信息的交换&#xff1b;而EGP工作在AS与AS之间&#xff0c;在AS间提供无环路的路由信息交换&#xff0c;BGP则是EGP的一种。 BGP是一…

打开深度学习的锁:(0)什么是神经网络?有哪些必备的知识点准备?

PS&#xff1a;每每温故必而知新 什么是神经网络&#xff1f; 一、一个单神经元的神经网络二、多个单神经元的神经网络三、到底什么是机器学习&#xff1f;&#xff08;重点&#xff09;1&#xff1a;什么是机器学习的训练&#xff1f;2&#xff1a;什么是模型&#xff1f;权重…

接口测试全流程扫盲..

一.为什么要做接口测试&#xff1f; ①.越底层发现bug&#xff0c;它的修复成本是越低的。 ②.前端随便变&#xff0c;接口测好了&#xff0c;后端不用变&#xff0c;前后端是两拨人开发的。 ③.检查系统的安全性、稳定性&#xff0c;前端传参不可信&#xff0c;比如京东购物…

拉链表实现过程+案例

第一种 1.从ODS层获取增量数据(上一天新增和更新的数据) 2.拿着DWD原始拉链表数据 left join 增量数据 ,修改原始拉链中历史数据的结束时间 3.拿着left join 的结果集 union all 增量数据 4.把最新的拉链数据优先保存到DWD对应的临时表中 5.使用insertselect 方式把临时表中…

哈希重要思想——位图详解

一&#xff0c;概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数据&#xff0c;数据无重复的场景。通常是用来判断某个数据存不存在的。 为了方便理解我们引入一道面试题&#xff0c; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无…

Python3 笔记:二进制的转换

十进制是逢十进一&#xff0c;二进制就是逢二进一。 十进制里最大的数字是9&#xff0c;二进制里最大的数字是1。 11010010001000010000010000001000000010^0110^11010^210010^3100010^41000010^510000010^6100000010^7100000002^012^122^242^382^4162^5322^6642^7128 1、十进…

ES扩缩容

ES扩容 1.1 页面扩容ES1 1.2 拷贝插件及ssl文件 JSON [ec_admin@kde-offline3 ~]$ sudo rsync -avP /usr/kde_ec/2.3.6.6-1/elasticsearch1/plugins/* kde-offline6:/usr/kde_ec/2.3.6.6-1/elasticsearch1/plugins/ ;echo $? [ec_admin@kde-offline3 ~]$ sudo rsync -avP /us…

【Python探索之旅】初识Python

目录 发展史&#xff1a; 环境安装&#xff1a; 入门案例&#xff1a; 变量类型 标准数据类型 数字类型&#xff1a; 字符串&#xff1a; 全篇总结&#xff1a; 前言&#xff1a; Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设…

GEE数据集——东南亚区域油棕种种植分布(油棕榈树种植园的概率)数据集

森林数据伙伴关系围绕对全球商品驱动的森林砍伐、森林退化和恢复工作的全球监测&#xff0c;加强合作与应用。 世界各国政府和公司都承诺帮助制止砍伐森林和加快恢复&#xff0c;以避免气候变化带来的最坏影响&#xff0c;防止生物多样性丧失&#xff0c;保护森林对人类和自然…

TriCore:Interrupt 2

今天继续来看看 IR 模块。 名词缩写 缩写全称说明IRInterrupt Router SRService Request 包括&#xff1a; 1. External Resource 2. Internal Resource 3.SW&#xff08;Software&#xff09; SPService Privoder 包括&#xff1a; 1. CPU 2. DMA SRNService Request NodeS…

给第一次接触产品设计的写的

设计概念 设计原则 你可以参考以下的原则在每次评估一个网站时&#xff0c;建立excel&#xff0c;包含两个表格 表格1&#xff1a; 启发式评估 评估人员姓名 设备/浏览器/OS 网站URL 日期容易修复度 (ER) E0.修复需要最大的努力 E1.修复需要相当大的努力 E2.修复需要一些努…

FPGA+HDMI转换方案,用于网络直播切换直播画面,客户应用:直播,自媒体

FPGAHDMI转换方案&#xff0c;用于网络直播切换直播画面 客户应用:直播&#xff0c;自媒体 主要功能: 1.支持多路HDMI高清输入/输出 2.支持各路输入输出灵活切换 3.支持USB接口 4.支持网口 5.支持音频输出接口 6.支持serders