// // demo 1 基础的多任务数据共享
// // 本例说明任务共享全局变量
// // 多任务间通过共享数据来进行通信
// // 通过共享全局变量来通信的实例往往应用在:
// // 一写多读
// // 就是对于全局变量,往往只有一个对象对它进行写操作,可以有多个变量进行读操作
// /*
// 程序: 任务之间通过全局变量进行传递数据
// 公众号:孤独的二进制
// 要求:数量类型-和CPU一致
// 写操作-有且只有一个任务
// 读操作-可以一个或者多个任务
// */
// // 养成良好习惯,被多进程和中断调用的变量使用 volatile 修饰符
// volatile uint32_t inventory = 100; //总库存 volatile全局
// volatile uint32_t retailCount = 0; //线下销售量
// // 零售任务
// void retailTask(void *pvParam) {
// while (1) {
// //以下实现了带有随机延迟的 inventory减1;
// //等效为 inventory--; retailCount++;
// uint32_t inv = inventory;
// for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i)); //随机延迟
// if (inventory > 0) {
// inventory = inv - 1;
// retailCount++;
// }
// };
// vTaskDelay(10); //老板要求慢一些,客户升级后,可以再加快速度
// }
// // 显示任务,显示当前的库存和销售额
// void showTask(void *pvParam) {
// while (1) {
// printf("Inventory : %d\n", inventory);
// printf(" Retail : %d\n", retailCount);
// if (inventory == 0 ) {
// printf("\n-----SALES SUMMARY-----\n");
// printf(" Total Sales: %d\n\n", retailCount);
// }
// vTaskDelay(pdMS_TO_TICKS(100));
// }
// }
// void setup() {
// // put your setup code here, to run once:
// Serial.begin(115200);
// xTaskCreate(retailTask,
// "Online Channel",
// 1024 * 4,
// NULL,
// 1,
// NULL);
// xTaskCreate(showTask,
// "Display Inventory",
// 1024 * 4,
// NULL,
// 1,
// NULL);
// }
// void loop() {
// }
// // 上面的程序,任务和任务之间,是共享着全局变量的,相当于实现了通信
// // 但是上述的任务共享变量方式存在一定的缺陷
// // 1. 上述的共享数据类型,必须等于CPU和内存的通道大小,esp32的通道是4个字节 因此共享数据必须是4字节(32比特)的
// // 2. arduino的int类型是2个字节的
// // 3. 因此引入了许多别名,uint32_t 这其实是给int typedef了 表示4字节32比特的int类型 u代表是正数
// // 4. 写操作 只能有一个任务
// // 中断 本质是一个函数 在setup中注册之后,每次满足中断条件就会自动执行中断函数
// // volatile 是挥发性变量
// // 正常的全局变量,只有在程序中被用到了,C++编译器才会给他分配内存空间,才会使用对应部分的内存
// // 而volatile是,只要编译器看到此变量,编译器就要给他分配空间,声明
// // 另外,volatile变量的读取,必须直接经过内存
// // 正常来说,变量的读取是首先从内存读取到寄存器,然后在寄存器中读取数据,也就是对于同一个变量的运算,我们只从内存读取一次放到寄存器中,下次再用时直接从寄存器读取,这样非常快。
// // 但是问题就是,如果对变量的操作在中途变了一次,就是对变量赋值或其他操作,此时该数据放到内存中,而操作时可能还在操作寄存器中原来的变量,就会造成错误
// // volitile的作用就是,这个数据我永远从内存直接读取,那么我只要任何时候对此变量做出了改变,我都可以得到及时的响应。
// // 因此volatile往往被用在全局变量上,确保数据的实时性
// //------------------------------------------------------
// // demo2 多任务、写操作
// /*
// 程序: 演示 多任务对 共享资源 同时使用的时候,出现的问题
// 多渠道销售,超卖的现象
// 公众号:孤独的二进制
// */
// // 养成良好习惯,被多进程和中断调用的变量使用 volatile 修饰符
// volatile uint32_t inventory = 100; //总库存
// // 两个写操作的全局变量
// volatile uint32_t retailCount = 0; //线下销售量
// volatile uint32_t onlineCount = 0; //线上销售量
// // 线下销售任务
// void retailTask(void *pvParam) {
// while (1) {
// //以下实现了带有随机延迟的 inventory减1;
// //等效为 inventory--; retailCount++;
// uint32_t inv = inventory;
// for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));
// if (inventory > 0) {
// inventory = inv - 1;
// retailCount++;
// }
// };
// vTaskDelay(10); //老板要求慢一些,客户升级后,可以再加快速度
// }
// // 线上销售
// void onlineTask(void *pvParam) {
// while (1) {
// //以下实现了带有随机延迟的 inventory减1;
// //等效为 inventory--; retailCount++;
// uint32_t inv = inventory;
// for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));
// if (inventory > 0) {
// inventory = inv - 1;
// onlineCount++;
// }
// vTaskDelay(10); //老板要求慢一些,客户升级后,可以再加快速度
// }
// }
// void showTask(void *pvParam) {
// while (1) {
// printf("Inventory : %d\n", inventory);
// printf(" Retail : %d, Online : %d\n", retailCount, onlineCount);
// if (inventory == 0 ) {
// uint32_t totalSales = retailCount + onlineCount;
// printf("-----SALES SUMMARY-----\n");
// printf(" Total Sales: %d\n", totalSales);
// printf(" OverSales: %d\n", 100 - totalSales); //多个写任务,可能会对数据过度写,超出范围
// // 超卖了
// }
// vTaskDelay(pdMS_TO_TICKS(1000));
// }
// }
// void setup() {
// // put your setup code here, to run once:
// Serial.begin(115200);
// xTaskCreate(onlineTask,
// "Online Channel",
// 1024 * 4,
// NULL,
// 1,
// NULL);
// xTaskCreate(retailTask,
// "Retail Channel",
// 1024 * 4,
// NULL,
// 1,
// NULL);
// xTaskCreate(showTask,
// "Display Inventory",
// 1024 * 4,
// NULL,
// 1,
// NULL);
// }
// void loop() {
// }
// -----------------------------------
// demo3 加锁 多任务
/*
程序: Tasks之间数据传递
有多任务同时写入,或者数据大小超过cpu内存通道(不是32比特了)时,或者对共享资源的访问时候,需要有防范机制
使用MUTEX对数据对Cirtical Section的内容进行保护
可以想象成MUTEX就是一把锁
公众号:孤独的二进制
语法:
SemaphoreHandle_t xHandler; 创建Handler
xHandler = xSemaphoreCreateMutex(); 创建一个MUTEX 返回NULL,或者handler
xSemaphoreGive(xHandler); 释放
xSemaphoreTake(xHanlder, timeout); 指定时间内获取信号量 返回pdPASS, 或者pdFAIL
理解方法:
MUTEX的工作原理可以想象成
共享的资源被锁在了一个箱子里,只有一把钥匙,有钥匙的任务才能对改资源进行访问
*/
// 养成良好习惯,被多进程和中断调用的变量使用 volatile 修饰符
volatile uint32_t inventory = 100; //总库存
volatile uint32_t retailCount = 0; //线下销售量
volatile uint32_t onlineCount = 0; //线上销售量
SemaphoreHandle_t xMutexInventory = NULL; //创建信号量Handler 创建此类型的句柄 一开始指向空
TickType_t timeOut = 1000; //用于获取信号量的Timeout 1000 ticks
void retailTask(void *pvParam) {
while (1) {
// 在timeout的时间内如果能够获取就继续
// 通俗一些:获取钥匙
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) { //这个函数表示 要在指定时间内,取得这个句柄,取到了返回pdPASS
//被MUTEX保护的内容叫做 Critical Section
//以下实现了带有随机延迟的 inventory减1;
//等效为 inventory--; retailCount++;
uint32_t inv = inventory;
for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));
if (inventory > 0) {
inventory = inv - 1;
retailCount++;
//释放钥匙
xSemaphoreGive(xMutexInventory); //释放句柄
} else {
//无法获取钥匙
}
};
vTaskDelay(100); //老板要求慢一些,客户升级后,可以再加快速度
}
}
void onlineTask(void *pvParam) {
while (1) {
// 在timeout的时间内如果能够获取二进制信号量就继续
// 通俗一些:获取钥匙
if (xSemaphoreTake(xMutexInventory, timeOut) == pdPASS) {
//被MUTEX保护的内容叫做 Critical Section
//以下实现了带有随机延迟的 inventory减1;
//等效为 inventory--; retailCount++;
uint32_t inv = inventory;
for (int i; i < random(10, 100); i++) vTaskDelay(pdMS_TO_TICKS(i));
if (inventory > 0) {
inventory = inv - 1;
onlineCount++;
//释放钥匙
xSemaphoreGive(xMutexInventory);
} else {
//无法获取钥匙
}
};
vTaskDelay(100); //老板要求慢一些,客户升级后,可以再加快速度
}
}
void showTask(void *pvParam) {
while (1) {
printf("Inventory : %d\n", inventory);
printf(" Retail : %d, Online : %d\n", retailCount, onlineCount);
if (inventory == 0 ) {
uint32_t totalSales = retailCount + onlineCount;
printf("-----SALES SUMMARY-----\n");
printf(" Total Sales: %d\n", totalSales);
printf(" OverSales: %d\n", 100 - totalSales);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
xMutexInventory = xSemaphoreCreateMutex(); //创建MUTEX 创建互斥锁 就是让句柄指向互斥锁(创建)
// 句柄本身就是起个标识作用,在此就是句柄指向了创建互斥锁函数(本质也是地址)
if (xMutexInventory == NULL) {
printf("No Enough Ram, Unable to Create Semaphore.");
} else {
xTaskCreate(onlineTask,
"Online Channel",
1024 * 4,
NULL,
1,
NULL);
xTaskCreate(retailTask,
"Retail Channel",
1024 * 4,
NULL,
1,
NULL);
xTaskCreate(showTask,
"Display Inventory",
1024 * 4,
NULL,
1,
NULL);
}
}
void loop() {
}