【FreeRTOS】同步互斥與通信 有缺陷的同步示例

目錄

  • 1 同步互斥與通信
    • 1.1 同步互斥與通信概述
    • 1.2 同步與互斥的概念
    • 1.3 同步的例子:有缺陷
    • 1.4 freertos.c源碼
      • 3. 互斥的例子:有缺陷
      • 4. 通信的例子:有缺陷
      • 5. FreeRTOS的解決方案


1 同步互斥與通信

1.1 同步互斥與通信概述

參考《FreeRTOS入門與工程實踐(基于DshanMCU-103)》里《第10章 同步互斥與通信》

本章是概述性的內容。可以把多任務系統當做一個團隊,里面的每一個任務就相當于團隊里的一個人。團隊成員之間要協調工作進度(同步)、爭用會議室(互斥)、溝通(通信)。多任務系統中所涉及的概念,都可以在現實生活中找到例子。

各類RTOS都會涉及這些概念:任務通知(task notification)、隊列(queue)、事件組(event group)、信號量(semaphoe)、互斥量(mutex)等。我們先站在更高角度來講解這些概念。

1.2 同步與互斥的概念

一句話理解同步與互斥:我等你用完廁所,我再用廁所。

什么叫同步?就是:哎哎哎,我正在用廁所,你等會。 什么叫互斥?就是:哎哎哎,我正在用廁所,你不能進來。

同步與互斥經常放在一起講,是因為它們之的關系很大,“互斥”操作可以使用“同步”來實現。我“等”你用完廁所,我再用廁所。這不就是用“同步”來實現“互斥”嗎?

再舉一個例子。在團隊活動里,同事A先寫完報表,經理B才能拿去向領導匯報。經理B必須等同事A完成報表,AB之間有依賴,B必須放慢腳步,被稱為同步。在團隊活動中,同事A已經使用會議室了,經理B也想使用,即使經理B是領導,他也得等著,這就叫互斥。經理B跟同事A說:你用完會議室就提醒我。這就是使用"同步"來實現"互斥"。

有時候看代碼更容易理解,偽代碼如下:

 void  搶廁所(void){if (有人在用) 我瞇一會;用廁所;喂,醒醒,有人要用廁所嗎;}

1.3 同步的例子:有缺陷

程序:在06_create_task_use_params的基礎上,修改出12_task_sync_exclusion

創建兩個任務:一個用來執行大量的計算任務,另一個永年執行打印函數顯示OLED

xTaskCreate(                //加返回值是 判斷任務有沒有創建成功CalcTask,           //計算任務"Task1",            //聲音任務128,                //棧大小NULL,       //傳入的參數 g_Task1InfoosPriorityNormal,   //優先級默認NULL                //任務句柄 無);
xTaskCreate(                //加返回值是 判斷任務有沒有創建成功LcdPrintTask,       //LCD打印任務"Task1",            //聲音任務128,                //棧大小&g_Task2Info,       //傳入的參數 g_Task1InfoosPriorityNormal,   //優先級默認NULL                //任務句柄 無);

編寫這兩個函數

static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};  // (0,0),Task1
static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"};  // (0,3),Task2
static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"};  // (0,6),Task3static int g_LCDCanUse = 1; //默認=1 能使用LCD
uint32_t g_sum = 0;   //定義一個計數值
static volatile int g_calc_end = 0; // 計算結束標志位,使用volatile不要用編譯器優化優化uint64_t g_time = 0;void CalcTask(void *params)
{uint32_t i = 0;     //定義一個計數值g_time = system_get_ns();   //獲取當前系統時間for (i = 0; i < 10000000; i ++){g_sum += i;}g_calc_end = 1; //計算完成標志位   置位g_time = system_get_ns() - g_time;  //運行這段任務消耗的時間vTaskDelete(NULL);  //計算完成殺死任務
}void LcdPrintTask(void *params)
{int len;while (1){LCD_PrintString(0, 0, "Waiting");// vTaskDelay(3000);while (g_calc_end == 0);    //等待/* 打印信息 */if (g_LCDCanUse){g_LCDCanUse = 0;LCD_ClearLine(0, 0);len = LCD_PrintString(0, 0, "Sum: ");LCD_PrintHex(len, 0, g_sum, 1);LCD_ClearLine(0, 2);len = LCD_PrintString(0, 2, "Time(ms): ");LCD_PrintSignedVal(len, 2, g_time/1000000); //打印消耗了多長時間g_LCDCanUse = 1;}vTaskDelete(NULL);  //任務自殺}
}

這里遇到了bug!!!

在這里插入圖片描述

  • 程序卡死在while循環里了,但是這個變量已經是1了,為什么程序會卡死在上面兩行匯編語句呢???

  • 原因是編譯器優化了我們的變量

對這個變量,執行while (g_calc_end == 0); 這條語句的時候,它會讀取內存,把變量的值放到CPU某個寄存器里,以后一直就判斷那個寄存器,但是這個寄存器得到的是原始的值,并沒有每次都去讀取內存

  • 這個變量是在其他任務里被修改的,那我們使用這個變量的時候,每次都需要讀內存!
  • 那怎么辦呢??我們加上一個volatile就可以了

燒錄代碼運行
在這里插入圖片描述

計算10000000個數需要2.5S,真的是這樣的嗎???

我們現在有兩個任務

在這里插入圖片描述
他們是怎么調度的呢?
兩個任務優先級相同,A任務運行1ms,B任務運行1ms,A任務運行1ms,B任務運行1ms

在這里插入圖片描述

任務B執行的程序是死等,這也耗費了一半的時間,在任務B的開始就等待3S左右

vTaskDelay(3000);   //開始的時候我先等待3000Tick

加上這句等待3S之后,就變成了1278ms

在這里插入圖片描述

確實如此

這個死循環可以用其他方法來代替

  • 等任務A計算完成之后,用任務A喚醒任務B
  • 使用同步的時候,我們需要考慮如何提高處理器的性能!
  • 讓等待的任務阻塞,不參與CPU的調度!

這節課學習了同步的例子,有缺陷的例子

學習視頻:【FreeRTOS入門與工程實踐 --由淺入深帶你學習FreeRTOS(FreeRTOS教程 基于STM32,以實際項目為導向)】 【精準空降到 13:38】 https://www.bilibili.com/video/BV1Jw411i7Fz/?p=25&share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933&t=818

1.4 freertos.c源碼

/* USER CODE BEGIN Header */
#include "driver_led.h"
#include "driver_lcd.h"
#include "driver_mpu6050.h"
#include "driver_timer.h"
#include "driver_ds18b20.h"
#include "driver_dht11.h"
#include "driver_active_buzzer.h"
#include "driver_passive_buzzer.h"
#include "driver_color_led.h"
#include "driver_ir_receiver.h"
#include "driver_ir_sender.h"
#include "driver_light_sensor.h"
#include "driver_ir_obstacle.h"
#include "driver_ultrasonic_sr04.h"
#include "driver_spiflash_w25q64.h"
#include "driver_rotary_encoder.h"
#include "driver_motor.h"
#include "driver_key.h"
#include "driver_uart.h"
#include "music.h"/********************************************************************************* File Name          : freertos.c* Description        : Code for freertos applications******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */BaseType_t ret; // long
static TaskHandle_t xSoundTaskHandle;           // void *  在全局變量里記錄句柄static StackType_t g_pucStackOfLightTask[128];  // 變量前綴的意思是 全局變量g 指針p uint8_t類型uc的StackOfLightTask 光任務的棧
StaticTask_t g_TCBofLightTask;                  // 光任務的TCB
static TaskHandle_t xLightTaskHandle;           // void *  在全局變量里記錄句柄static StackType_t g_pucStackOfColorTask[128];  // 變量前綴的意思是 全局變量g 指針p uint8_t類型uc的StackOfLightTask 色任務的棧
StaticTask_t g_TCBofColorTask;                  // 色任務的TCB
static TaskHandle_t xColorTaskHandle;           // void *  在全局變量里記錄句柄/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {.name = "defaultTask",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
};/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */struct TaskPrintInfo{uint8_t x;      //定義坐標xuint8_t y;      //定義坐標ychar name[16];  //定義要打印輸出的內容,最多顯示16個字符
};static struct TaskPrintInfo g_Task1Info = {0, 0, "Task1"};  // (0,0),Task1
static struct TaskPrintInfo g_Task2Info = {0, 3, "Task2"};  // (0,3),Task2
static struct TaskPrintInfo g_Task3Info = {0, 6, "Task3"};  // (0,6),Task3static int g_LCDCanUse = 1; //默認=1 能使用LCD
uint32_t g_sum = 0;   //定義一個計數值
static volatile int g_calc_end = 0; // 計算結束標志位,使用volatile不要用編譯器優化優化uint64_t g_time = 0;void CalcTask(void *params)
{uint32_t i = 0;     //定義一個計數值g_time = system_get_ns();   //獲取當前系統時間for (i = 0; i < 10000000; i ++){g_sum += i;}g_calc_end = 1; //計算完成標志位   置位g_time = system_get_ns() - g_time;  //運行這段任務消耗的時間vTaskDelete(NULL);  //計算完成殺死任務
}void LcdPrintTask(void *params)
{int len;while (1){LCD_PrintString(0, 0, "Waiting");vTaskDelay(3000);   //開始的時候我先等待3000Tickwhile (g_calc_end == 0);    //等待/* 打印信息 */if (g_LCDCanUse){g_LCDCanUse = 0;LCD_ClearLine(0, 0);len = LCD_PrintString(0, 0, "Sum: ");LCD_PrintHex(len, 0, g_sum, 1);LCD_ClearLine(0, 2);len = LCD_PrintString(0, 2, "Time(ms): ");LCD_PrintSignedVal(len, 2, g_time/1000000); //打印消耗了多長時間g_LCDCanUse = 1;}vTaskDelete(NULL);  //任務自殺}
}
/* USER CODE END FunctionPrototypes */void StartDefaultTask(void *argument);void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//*** @brief  FreeRTOS initialization* @param  None* @retval None*/
void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init */LCD_Init();LCD_Clear();/* USER CODE END Init *//* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* creation of defaultTask *///  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *///  /* 創建任務:聲 */
//  // 先創建一個動態分配內存的任務
//  ret = xTaskCreate(                //加返回值是 判斷任務有沒有創建成功
//            PlayMusic,          //孤勇者的函數
//            "SoundTask",        //聲音任務
//            128,                //棧大小
//            NULL,               //無傳入的參數
//            osPriorityNormal,   //優先級默認
//            & xSoundTaskHandle  //任務句柄
//            );//  
//  /* 創建任務:光 */ 
//  // 創建一個靜態分配內存的任務
//  xLightTaskHandle = xTaskCreateStatic(
//            Led_Test,           //LED測試函數,PC13以500ms間隔亮滅一次
//            "LightTask",        //光任務
//            128,                //棧大小,這里提供了棧的大小(長度)
//            NULL,               //無傳入的參數
//            osPriorityNormal,   //優先級默認
//            g_pucStackOfLightTask,  // 靜態分配的棧,一個buffer,這里只提供了首地址,長度就是棧的大小,最開始棧的類型不對,棧的類型uint32_t
//            &g_TCBofLightTask       // 取址TCB
//  );
//  
//  /* 創建任務:色 */ 
//  xColorTaskHandle = xTaskCreateStatic(
//            ColorLED_Test,           //LED測試函數,PC13以500ms間隔亮滅一次
//            "ColorTask",        //光任務
//            128,                //棧大小,這里提供了棧的大小(長度)
//            NULL,               //無傳入的參數
//            osPriorityNormal,   //優先級默認
//            g_pucStackOfColorTask,  // 靜態分配的棧,一個buffer,這里只提供了首地址,長度就是棧的大小
//            &g_TCBofColorTask       // 取址TCB
//  );xTaskCreate(                //加返回值是 判斷任務有沒有創建成功CalcTask,           //計算任務"Task1",            //聲音任務128,                //棧大小NULL,       //傳入的參數 g_Task1InfoosPriorityNormal,   //優先級默認NULL                //任務句柄 無);xTaskCreate(                //加返回值是 判斷任務有沒有創建成功LcdPrintTask,       //LCD打印任務"Task1",            //聲音任務128,                //棧大小&g_Task2Info,       //傳入的參數 g_Task1InfoosPriorityNormal,   //優先級默認NULL                //任務句柄 無);/* USER CODE END RTOS_THREADS *//* USER CODE BEGIN RTOS_EVENTS *//* add events, ... *//* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask *//*** @brief  Function implementing the defaultTask thread.* @param  argument: Not used* @retval None*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{/* USER CODE BEGIN StartDefaultTask *//* Infinite loop */LCD_Init();LCD_Clear();for(;;){//Led_Test();//LCD_Test();//MPU6050_Test(); //DS18B20_Test();//DHT11_Test();//ActiveBuzzer_Test();//PassiveBuzzer_Test();//ColorLED_Test();IRReceiver_Test();  //影//IRSender_Test();//LightSensor_Test();//IRObstacle_Test();//SR04_Test();//W25Q64_Test();//RotaryEncoder_Test();//Motor_Test();//Key_Test();//UART_Test();}/* USER CODE END StartDefaultTask */
}/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application *//* USER CODE END Application */

在這里插入圖片描述

3. 互斥的例子:有缺陷

講解這個程序"06_create_task_use_params"的互斥缺陷。

4. 通信的例子:有缺陷

5. FreeRTOS的解決方案

  • 正確性

  • 效率:等待者要進入阻塞狀態

  • 多種解決方案

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40223.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40223.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40223.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

搞錢四步:干活、出名、破圈、整合

搞錢這事兒&#xff0c;說起來有四步&#xff1a;干活、出名、破圈、整合。 咱們現在這個時代&#xff0c;誰要是能把自尊心放一邊&#xff0c;勇敢站到舞臺上展示才華&#xff0c;變現那是分鐘的事兒。 只要你敢承認自己想要財富&#xff0c;并且不停地使用正確的方法論&…

python語句前面有一個$是什么意思

“$”是匯編語言中的一個預定義符號&#xff0c;等價于當前正匯編到的段的當前偏移值。例如&#xff1a;指令“jmp $3”中的“$”表示當前這條指令在代碼段中的偏移量。 代表當前指令的地址&#xff0c;如&#xff1a; data segment str1 db a,b,c,d leng equ $-str 就是當前地…

JVM專題之性能優化

運行時優化 方法內聯 > 方法內聯,是指 **JVM在運行時將調用次數達到一定閾值的方法調用替換為方法體本身** ,從而消除調用成本,并為接下來進一步的代碼性能優化提供基礎,是JVM的一個重要優化手段之一。 > > **注:** > > * **C++的inline屬于編譯后內聯,…

數據庫實訓復習(1)

目錄 一、關于表結構的相關操作&#xff08;與表中字段的數據操作有區別&#xff09; &#xff08;1&#xff09;往已有的表中添加字段 &#xff08;2&#xff09;修改表中已有字段的數據類型 &#xff08;3&#xff09;修改已有的表中的字段名和字段類型 &#xff08;4&a…

TopK問題與堆排序

目錄 TopK問題&#xff1a; 定義&#xff1a; 應用場景&#xff1a; 搜索引擎&#xff1a; 推薦系統&#xff1a; 數據分析&#xff1a; 數據挖掘&#xff1a; TopK問題初階&#xff1a;&#xff08;數據量較小情況&#xff09; TopK問題進階&#xff1a;&#xff08;…

知名品牌因商標痛失市場:114家直營店山寨店7000多家!

奶茶知名品牌“鹿角巷”當年紅遍大江南北&#xff0c;是最早的新茶飲品牌&#xff0c;但是當年商標注冊存在問題&#xff0c;被同行奶茶品牌搶占了先機&#xff0c;發聲明“對大陸商標注冊細則不詳&#xff0c;在商標注冊過程中讓假店鉆了法律空檔”&#xff0c;最夸張的時候全…

qml required property

目錄 前言 示例代碼 創建一個自定義組件&#xff08;MyComponent.qml&#xff09; 使用自定義組件&#xff08;main.qml&#xff09; 解釋 運行效果 運行時錯誤示例 前言 在 QML 中&#xff0c;你可以使用 required 關鍵字來聲明一個屬性是必需的。這意味著在創建該對象…

如何用Python向PPT中批量插入圖片

辦公自動化辦公中&#xff0c;Python最大的優勢是可以批量操作&#xff0c;省去了用戶粘貼、復制、插入等繁瑣的操作。經常做PPT的朋友都知道&#xff0c;把圖片插入到PPT當中的固定位置是一個非常繁瑣的操作&#xff0c;往往調整圖片時耗費大量的時間和精力。如何能省時省力插…

【數據結構】使用C語言 從零實現一個棧的數據結構

棧 什么是棧&#xff1f;棧是一種特殊的線性表&#xff0c;它只能在在表尾進行插入和刪除操作。 棧的底部稱為棧底&#xff0c;頂部稱為棧頂&#xff0c;所有的操作只能在棧頂進行&#xff0c;也就是說&#xff0c;被壓在下方的元素&#xff0c;只能等待其上方的元素出棧之后…

LeetCode-簡單-回文數

給你一個整數 x &#xff0c;如果 x 是一個回文整數&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。 回文數 是指正序&#xff08;從左向右&#xff09;和倒序&#xff08;從右向左&#xff09;讀都是一樣的整數。 例如&#xff0c;121 是回文&#xff0c;…

windows啟動Docker閃退Docker desktop stopped

Windows啟動Docker閃退-Docker desktop stopped 電腦上很早就安裝有Docker了&#xff0c;但是有一段時間都沒有啟動了&#xff0c;今天想啟動啟動不起來了&#xff0c;打開沒幾秒就閃退&#xff0c;記錄一下解決方案。僅供參考 首先&#xff0c;參照其他解決方案&#xff0c;本…

Ubuntu20安裝mysql方法,適用于wsl

itopen組織1、提供OpenHarmony優雅實用的小工具2、手把手適配riscv qemu linux的三方庫移植3、未來計劃riscv qemu ohos的三方庫移植 小程序開發4、一切擁抱開源&#xff0c;擁抱國產化 一、Ubunt20安裝mysql 適用于wsl中安裝mysql sudo apt update# 查看可使用的安裝包…

【刷題匯總--游游的you、腐爛的蘋果、孩子們的游戲(圓圈中最后剩下的數)】

C日常刷題積累 今日刷題匯總 - day0051、游游的you1.1、題目1.2、思路1.3、程序實現 - 蠻力法1.4、程序實現 - 貪心(優化) 2、腐爛的蘋果2.1、題目2.2、思路2.3、程序實現 - bfs 3、孩子們的游戲(圓圈中最后剩下的數)3.1、題目3.2、思路3.3、程序實現 -- 環形鏈表3.4、程序實現…

2個方法教你輕松移除pdf文件編輯限制

PDF是一種常見的辦公文檔格式&#xff0c;常用于文件共享和保護。然而&#xff0c;有時候我們需要編輯PDF文件中的內容&#xff0c;但受到了編輯限制。本文將介紹一些有效的方法&#xff0c;幫助您解除PDF的編輯限制&#xff0c;輕松進行編輯和修改。 一、通過密碼取消PDF“限制…

雷電模擬器報錯remount of the / superblock failed: Permission denied remount failed

報錯截圖 解決方法 打開設置 設置配置system.vmdk可寫入 解決

Transformer和Mamba強強結合!最新混合架構全面開源,推理速度狂飆8倍

最近發現&#xff0c;將Mamba和Transformer模塊混合使用&#xff0c;效果會比單獨使用好很多&#xff0c;這是因為該方法結合了Mamba的長序列處理能力和Transformer的建模能力&#xff0c;可以顯著提升計算效率和模型性能。 典型案例如大名鼎鼎的Jamba&#xff1a;Jamba利用Tr…

ELK優化之Elasticsearch

目錄 1.ELK優化 2.優化 ES 索引設置 2.1 優化 fsync 2.2 優化 refresh 2.3 優化 merge 2.4 優化設置 2.5 打開索引 3.優化線程池配置 3.1 優化的方案 4.鎖定內存&#xff0c;不讓 JVM 使用 Swap 5.減少分片數、副本數 6.ES優化總結 1.ELK優化 ELK優化可以圍繞著 li…

Python統計實戰:時間序列分析之簡單指數平滑和Holt指數平滑

為了解決特定問題而進行的學習是提高效率的最佳途徑。這種方法能夠使我們專注于最相關的知識和技能&#xff0c;從而更快地掌握解決問題所需的能力。 &#xff08;以下練習題來源于《統計學—基于Python》。請在Q群455547227下載原始數據。&#xff09; 練習題 下表是某只股票…

二維平面無中心點的聚類算法

問題描述 二維平面上有許多點p(x , y)&#xff0c;按照彼此之間的歐式距離進行分為若干個集合。若點p1(x1, y1)與點p(x2, y2)之間距離小于d,則認為二者是鄰居。 算法思路 給數據集的點進行編號&#xff0c;順序遍歷這些點&#xff0c;找出當前點的鄰居&#xff0c;記住已經遍…

模具監視器的選擇要點介紹

模具監視器的選擇要點涉及多個方面&#xff0c;以確保其能夠滿足實際生產需求并提高生產效率。以下是一些關鍵的選擇要點&#xff1a; 一、性能和穩定性 監控精度&#xff1a;選擇模具監視器時&#xff0c;首先要考慮其監控精度&#xff0c;包括溫度、壓力、注射速度等參數的…