任务通知理论和使用

文章目录

  • 一、任务通知是什么?
    • 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.hqwc.cn/news/697376.html

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

相关文章

AI如何改变PPT制作流程?推荐四款ai生成ppt工具

提起PPT&#xff0c;大家的第一反应就是痛苦。经常接触PPT的学生党和打工人&#xff0c;光看到这3个字母&#xff0c;就已经开始头痛了&#xff1a; 1、PPT内容框架与文案挑战重重&#xff0c;任务艰巨&#xff0c;耗费大量精力。 2、PPT的排版技能要求高&#xff0c;并非易事…

centos7中如何全局搜索一下nginx的配置文件?

在CentOS 7中搜索Nginx的配置文件&#xff0c;你可以使用一些常用的命令行工具&#xff0c;比如find、grep等。这些工具可以帮助你在文件系统中查找文件&#xff0c;也可以用来查找Docker容器内部的文件&#xff0c;只要你知道如何访问容器的文件系统。 1. 搜索系统中的Nginx配…

深度剖析深度神经网络(DNN):原理、实现与应用

目录 引言 一、DNN基本原理 二、DNN核心算法原理 三、DNN具体操作步骤 四、代码演示 引言 在人工智能和机器学习的浪潮中&#xff0c;深度神经网络&#xff08;Deep Neural Network&#xff0c;简称DNN&#xff09;已经成为了一种非常重要的工具。DNN模仿人脑神经网络的结…

C语言leetcode刷题笔记3

C语言leetcode刷题笔记3 第8题&#xff1a;876.链表的中间结点遍历数节点个数快慢指针 第9题&#xff1a;874.比较含退格的字符串第10题&#xff1a;155.最小栈法1&#xff1a;getMin内部实现查找法2&#xff1a;getmin直接返回值补充&#xff1a;栈的使用例子优化&#xff1a;…

Leetcode2079. 给植物浇水

Every day a Leetcode 题目来源&#xff1a;2079. 给植物浇水 解法1&#xff1a;模拟 sum 记录总步数&#xff0c;初始化为 0。water 表示当前水量&#xff0c;初始化为 capacity。 遍历 plants 数组&#xff0c;设下标为 i 的植物需要的水量 plant plants[i]。 如果 wat…

2023 PWNHUB 3月赛-【tototo】

文章目录 volatile泄露_environ打栈漏洞利用malloc和calloc思路&#xff08;打_environ&#xff09;代码 volatile int volatile vInt; 当要求使用 volatile 声明的变量值的时候&#xff0c;系统总是重新从它所在的内存读取数据&#xff0c;即使它前面的指令刚刚从该处读取过数…

孙宇晨对话大公网:香港Web3政策友好环境示范意义重大

日前,全球知名华文媒体大公网发布《湾区web3大有可为》重磅系列报道。报道通过对中国香港与大湾区其他城市Web3政策、行业创新和生态建设等方面的梳理,以及对行业领袖和重要行业机构的走访,全面展现了在大湾区一体化发展的背景下,Web3等数字经济模式在该地区的长远发展潜力。 …

GitHub和huggingface镜像网站

GitHub镜像网站 gitclone 如果网络原因打不开GitHub的话&#xff0c;可以用这个网站进行克隆项目&#xff0c;将克隆代码修改一下 git clone https://github.com/comfyanonymous/ComfyUI.git 修改 git clone https://gitclone.com/github.com/comfyanonymous/ComfyUI.git 这个…

[附源码]剑灵三系可乐6.1_Win服务端_联网+单机搭建

本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#xff0c;踩过的坑都给你们填上了。 如果你是小白也没…

depcheck检查项目中未被使用的依赖

depcheck是一个用于分析项目中依赖项的工具&#xff0c;可以查看&#xff1a;每个依赖项是如何使用的&#xff0c;哪些依赖项是无用的&#xff0c;以及哪些依赖项在package.json 1、安装 npm install -g depcheck # 必须全局安装2、可配置文件.depcheckrc&#xff08;不配置 直…

Django图书馆综合项目-学习

图书馆项目 一 前期准备 安装好所需的环境 我这边用的IDE是VScode 操作系统是MACOS 二 先创建一个虚拟环境 python3 -m venv myenvbook source myenvbook/bin/activate 三 安装 Django pip3 install django4.2 四 创建 Django 项目 django-admin startproject bookp…

简单实现---基于STL的演讲比赛流程管理系统(C++实现)

前言 事先声明&#xff1a;本文章中编写的代码仅用于学习算法思想和编写基础形式使用&#xff0c;并未进行太多的代码优化&#xff0c;因此&#xff0c;若需要对代码进行优化以及异常处理的小伙伴们&#xff0c;可自行添加相关操作&#xff0c;谢谢&#xff01; 一、题…