FreeRTOS教程6 互斥量

来源:https://www.cnblogs.com/lc-guo/p/18068439
-Advertisement-
Play Games

本文主要學習 FreeRTOS 互斥量的相關知識,包括優先順序翻轉問題、優先順序繼承、死鎖現象、創建/刪除互斥量 和 獲取/釋放互斥量等知識 ...


1、準備材料

正點原子stm32f407探索者開發板V2.4

STM32CubeMX軟體(Version 6.10.0

Keil µVision5 IDE(MDK-Arm

野火DAP模擬器

XCOM V2.6串口助手

2、學習目標

本文主要學習 FreeRTOS 互斥量的相關知識,包括優先順序翻轉問題、優先順序繼承、死鎖現象、創建/刪除互斥量 和 獲取/釋放互斥量等知識

3、前提知識

3.1、優先順序翻轉問題

使用二值信號量用於進程間同步時可能會出現優先順序翻轉的問題,什麼是“優先順序翻轉”問題呢?考慮如下所述的任務運行過程

  • 在 t1 時刻,低優先順序的任務 TaskLP 切入運行狀態,並且獲取到了一個二值信號量 Binary Semaphores
  • 在 t2 時刻,高優先順序的任務 TaskHP 請求獲取二值信號量 Binary Semaphores ,但是由於 TaskLP 還未釋放該二值信號量,所以在 t3 時刻,任務 TaskHP 進入阻塞狀態等待二值信號量被釋放
  • 在 t4 時刻,中等優先順序的任務 TaskMP 進入就緒狀態,由於不需要獲取二值信號量,因此搶占低優先順序任務任務 TaskLP 切入運行狀態
  • 在 t5 時刻,任務 TaskMP 運行結束,任務 TaskLP 再次切入運行狀態
  • 在 t6 時刻,任務 TaskLP 運行結束,釋放二值信號量 Binary Semaphores,此時任務 TaskHP 從等待二值信號量的阻塞狀態切入運行狀態
  • 在t7時刻,任務 TaskHP 運行結束

根據上述流程讀者可以發現一個問題,即在 t4 時刻中等優先順序的任務 TaskMP 先於高優先順序的任務 TaskHP 搶占了處理器,這破壞了 FreeRTOS 基於優先順序搶占式執行的原則,我們將這種情況稱為優先順序翻轉問題,上述描述的任務運行過程具體時刻流程圖如下圖所示

優先順序翻轉可能是一個嚴重的問題,但在小型嵌入式系統中,通常可以在系統設計時通過考慮如何訪問資源來避免該問題

3.2、優先順序繼承

為瞭解決使用二值信號量可能會出現的優先順序翻轉問題,對二值信號量做了改進,增加了一種名為 “優先順序繼承” 的機制,改進後的實例稱為了互斥量,註意雖然互斥量可以減緩優先順序翻轉問題的出現,但是並不能完全杜絕

接下來我們來通過例子介紹什麼是優先順序繼承?

仍然考慮由 “3.1、優先順序翻轉問題” 小節中提出的任務運行過程的例子,具體流程如下所述,讀者可以細心理解其中的不同之處

  • 在 t1 時刻,低優先順序的任務 TaskLP 切入運行狀態,並且獲取到了一個互斥量 Mutexes
  • 在 t2 時刻,高優先順序的任務 TaskHP 請求獲取互斥量 Mutexes ,但是由於 TaskLP 還未釋放該互斥量,所以在 t3 時刻,任務 TaskHP 進入阻塞狀態等待互斥量被釋放,但是與二值信號量不同的是,此時 FreeRTOS 將任務 TaskLP 的優先順序臨時提高到與任務 TaskHP 一致的優先順序,也即高優先順序
  • 在 t4 時刻,中等優先順序的任務 TaskMP 進入就緒狀態發生任務調度,但是由於任務 TaskLP 此時優先順序被提高到了高優先順序,因此任務 TaskMP 仍然保持就緒狀態等待優先順序較高的任務執行完畢
  • 在 t5 時刻,任務 TaskLP 執行完畢釋放互斥量 Mutexes,此時任務 TaskHP 搶占處理器切入運行狀態,並恢復任務 TaskLP 原來的優先順序
  • 在 t6 時刻,任務 TaskHP 執行完畢,此時輪到任務 TaskMP 執行
  • 在 t7 時刻,任務 TaskMP 運行結束

根據互斥量的上述任務流程讀者可以發現與二值信號量的不同之處,上述描述的任務運行過程具體時刻流程圖如下圖所示

3.3、什麼是互斥量?

互斥量/互斥鎖是一種特殊類型的二進位信號量,用於控制對在兩個或多個任務之間共用資源的訪問

互斥鎖可以被視為一個與正在共用的資源相關聯的令牌,對於合法訪問資源的任務,它必須首先成功 “獲取” 令牌,成為資源的持有者,當持有者完成對資源的訪問之後,其需要 ”歸還” 令牌,只有 “歸還” 令牌之後,該令牌才可以再次被其他任務所 “獲取” ,這樣保證了互斥的對共用資源的訪問,上述機制如下圖所示 (註釋1)

3.4、死鎖現象

“死鎖” 是使用互斥鎖進行互斥的另一個潛在陷阱,當兩個任務因為都在等待對方占用的資源而無法繼續進行時,就會發生死鎖,考慮如下所述的情況

  1. 任務 A 執行併成功獲取互斥量 X
  2. 任務 A 被任務 B 搶占
  3. 任務 B 在嘗試獲取互斥量 X 之前成功獲取互斥量 Y,但互斥量 X 由任務 A 持有,因此對任務 B 不可用,任務 B 選擇進入阻塞狀態等待互斥量 X 被釋放
  4. 任務 A 繼續執行,它嘗試獲取互斥量 Y,但互斥量 Y 由任務 B 持有,所以對於任務 A 來說是不可用的,任務 A 選擇進入阻塞狀態等待待釋放的互斥量 Y

經過上述的這樣一個過程,讀者可以發現任務 A 在等待任務 B 釋放互斥量 Y ,而任務 B 在等待任務 A 釋放互斥量 X ,兩個任務都在阻塞狀態無法執行,從而導致 ”死鎖“ 現象的發生,與優先順序翻轉一樣,避免 “死鎖” 的最佳方法是在設計時考慮其潛在影響,並設計系統以確保不會發生死鎖

3.5、什麼是遞歸互斥量?

任務也有可能與自身發生死鎖,如果任務嘗試多次獲取相同的互斥體而不首先返回互斥體,就會發生這種情況,考慮以下設想:

  1. 任務成功獲取互斥鎖
  2. 在持有互斥體的同時,任務調用庫函數
  3. 庫函數的實現嘗試獲取相同的互斥鎖,併進入阻塞狀態等待互斥鎖變得可用

在此場景結束時,任務處於阻塞狀態以等待互斥體返回,但任務已經是互斥體持有者。 由於任務處於阻塞狀態等待自身,因此發生了死鎖

通過使用遞歸互斥體代替標準互斥體可以避免這種類型的死鎖,同一任務可以多次 “獲取” 遞歸互斥鎖,並且只有在每次 “獲取” 遞歸互斥鎖之後都調用一次 “釋放” 遞歸互斥鎖,才會返回該互斥鎖

因此遞歸互斥量可以視為特殊的互斥量,一個互斥量被一個任務獲取之後就不能再次獲取,其他任務想要獲取該互斥量必須等待這個任務釋放該互斥連,但是遞歸互斥量可以被一個任務重覆獲取多次,當然每次獲取必須與一次釋放配對使用

註意不管是互斥量,還是遞歸互斥量均存在優先順序繼承機制,但是由於 ISR 並不是任務,因此互斥量和遞歸互斥量不能在中斷中使用

3.5、創建互斥量

互斥量在使用之前必須先創建,因為互斥量分為互斥量和遞歸互斥量兩種,所以 FreeRTOS 也提供了不同的 API 函數,具體如下所述

/**
  * @brief  動態分配記憶體創建互斥信號量函數
  * @retval 創建互斥信號量的句柄
  */
SemaphoreHandle_t xSemaphoreCreateMutex(void);

/**
  * @brief  靜態分配記憶體創建互斥信號量函數
  * @param  pxMutexBuffer:指向StaticSemaphore_t類型的變數,該變數將用於保存互斥鎖型信號量的狀態
  * @retval 返回成功創建後的互斥鎖的句柄,如果返回NULL則表示記憶體不足創建失敗
  */
SemaphoreHandle_t xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxMutexBuffer);

/**
  * @brief  動態分配記憶體創建遞歸互斥信號量函數
  * @retval 創建遞歸互斥信號量的句柄,如果返回NULL則表示記憶體不足創建失敗
  */
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);

/**
  * @brief  動態分配記憶體創建二值信號量函數
  * @param  pxMutexBuffer:指向StaticSemaphore_t類型的變數,該變數將用於保存互斥鎖型信號量的狀態
  */
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(
								StaticSemaphore_t pxMutexBuffer);

3.6、獲取互斥量

獲取互斥量直接使用獲取信號量的函數即可,但對於遞歸互斥量需要專門的獲取函數,具體如下所述

/**
  * @brief  獲取信號量函數
  * @param  xSemaphore:正在獲取的信號量的句柄
  * @param  xTicksToWait:等待信號量變為可用的時間
  * @retval 成功獲取信號量則返回pdTRUE, xTicksToWait過期,信號量不可用,則返回pdFALSE
  */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait);

/**
  * @brief  獲取遞歸互斥量
  * @param  xMutex:正在獲得的互斥鎖的句柄
  * @param  xTicksToWait:等待信號量變為可用的時間
  * @retval 成功獲取信號量則返回pdTRUE, xTicksToWait過期,信號量不可用,則返回pdFALSE
  */
BaseType_t xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex,
								   TickType_t xTicksToWait);

3.7、釋放互斥量

釋放互斥量直接使用釋放信號量的函數即可,但對於遞歸互斥量需要專門的釋放函數,具體如下所述

/**
  * @brief  釋放信號量函數
  * @param  xSemaphore:要釋放的信號量的句柄
  * @retval 成功釋放信號量則返回pdTRUE, 若發生錯誤,則返回pdFALSE
  */
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);

/**
  * @brief  釋放遞歸互斥量
  * @param  xMutex:正在釋放或“給出”的互斥鎖的句柄
  * @retval 成功釋放遞歸互斥量後返回pdTRUE
  */
BaseType_t xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex);

3.8、刪除互斥量

直接使用信號量的刪除函數即可,具體如下所述

/**
  * @brief  獲取信號量函數
  * @param  xSemaphore:要刪除的信號量的句柄
  * @retval None
  */
void vSemaphoreDelete(SemaphoreHandle_t xSemaphore);

4、實驗一:優先順序翻轉問題

4.1、實驗目標

既然實驗是討論優先順序翻轉問題,那麼我們來複現 “3.1、優先順序翻轉問題” 小節中所描述到的任務運行過程,具體如下所述

  1. 創建一個二值信號量 BinarySem_PI,用於演示優先順序翻轉問題
  2. 創建一個低優先順序任務 Task_Low ,在該任務中獲取二值信號量 BinarySem_PI ,並通過延時模擬長時間連續運行,運行結束後釋放該二值信號量,整個過程會通過串口輸出提示信息
  3. 創建一個中等優先順序任務 Task_Middle,該任務負責在 Task_Low 模擬長時間連續運行期間搶占其處理器控制許可權
  4. 創建一個高優先順序任務 Task_High,該任務總是嘗試獲取二值信號量 BinarySem_PI

4.2、CubeMX相關配置

首先讀者應按照"FreeRTOS教程1 基礎知識"章節配置一個可以正常編譯通過的 FreeRTOS 空工程,然後在此空工程的基礎上增加本實驗所提出的要求

本實驗需要初始化 USART1 作為輸出信息渠道,具體配置步驟請閱讀“STM32CubeMX教程9 USART/UART 非同步通信”,如下圖所示

單擊 Middleware and Software Packs/FREERTOS,在 Configuration 中單擊 Tasks and Queues 選項卡雙擊預設任務修改其參數,然後增加另外兩個不同優先順序的任務,具體如下圖所示

然後在 Configuration 中單擊 Timers and Semaphores ,在 Binary Semaphores 中單擊 Add 按鈕新增加一個名為 BinarySem_PI 的二值信號量,具體如下圖所示

配置 Clock Configuration 和 Project Manager 兩個頁面,接下來直接單擊 GENERATE CODE 按鈕生成工程代碼即可

4.3、添加其他必要代碼

按照 “STM32CubeMX教程9 USART/UART 非同步通信” 實驗 “6、串口printf重定向” 小節增加串口 printf 重定向代碼,具體不再贅述

首先應該在 freertos.c 中添加信號量相關 API 和 printf() 函數的頭文件,如下所述

/*freertos.c中添加頭文件*/
#include "semphr.h"
#include "stdio.h"

然後在該文件中實現三個不同優先順序的任務,主要是一些串口輸出給用戶的提示信息,方便演示實驗目的,具體如下所述

/*低優先順序任務*/
void AppTask_Low(void *argument)
{
  /* USER CODE BEGIN AppTask_Low */
  /* Infinite loop */
	uint8_t str1[]="Task_Low take it\r\n";
	uint8_t str2[]="Task_Low give it\r\n";
	uint8_t str3[]="return Task_Low\r\n";
	for(;;)
	{
		//獲取信號量
		if(xSemaphoreTake(BinarySem_PIHandle, pdMS_TO_TICKS(200))==pdTRUE)  
		{
			printf("%s",str1);
			//模擬任務連續運行
			HAL_Delay(500);		
			printf("%s",str3);
			HAL_Delay(500);
			printf("%s",str2);
			//釋放信號量
			xSemaphoreGive(BinarySem_PIHandle);		
		}
	}
  /* USER CODE END AppTask_Low */
}

/*中等優先順序任務*/
void AppTask_Middle(void *argument)
{
  /* USER CODE BEGIN AppTask_Middle */
  /* Infinite loop */
	uint8_t strMid[]="Task_Middle is running\r\n";
	for(;;)
	{
		printf("%s", strMid);
		vTaskDelay(500);
	}
  /* USER CODE END AppTask_Middle */
}

/*高優先順序任務*/
void AppTask_High(void *argument)
{
  /* USER CODE BEGIN AppTask_High */
  /* Infinite loop */
	uint8_t strHigh1[]="Into Task_High\r\n";
	uint8_t strHigh2[]="Task_High get token\r\n";
	uint8_t strHigh3[]="Task_High give token\r\n";
	for(;;)
	{
		printf("%s",strHigh1);
		//獲取信號量
		if(xSemaphoreTake(BinarySem_PIHandle, portMAX_DELAY)==pdTRUE)  
		{
			printf("%s",strHigh2);
			printf("%s",strHigh3);
			//釋放信號量
			xSemaphoreGive(BinarySem_PIHandle);	
		}
		vTaskDelay(500);
	}
  /* USER CODE END AppTask_High */
}

在 "FreeRTOS教程5 信號量" 文章 ”3.2、創建信號量“ 小節中曾提到,信號量被創建完之後是無效的,但是這裡我們需要讓剛創建的二值信號量有效,否則 Task_High 和 Task_Low 都將無法獲取二值信號量,因此最後修改二值信號量的初始值為 1 即可,具體如下所示

/*將初始值0修改為1*/
BinarySem_PIHandle = osSemaphoreNew(1, 1, &BinarySem_PI_attributes);

4.4、燒錄驗證

燒錄程式,打開串口助手,按住開發板複位按鍵,目的是為了讓串口助手接收程式從最開始輸出的信息,這裡我們只分析第一輪,因為延時、語句執行等微小的時間差異會導致第二輪任務進入阻塞和退出阻塞的時間與第一輪有差異,如下所述為第一輪詳細的任務執行流程

  1. 當創建完三個不同優先順序的任務後不會立即得到執行,而是進入就緒狀態等待調度器的啟動
  2. 當調度器啟動之後會按照優先順序從最高優先順序開始執行,因此串口輸出 “Into Task_High” 表示進入高優先順序任務,然後在高優先順序任務 Task_High 中獲得二值信號量,然後立馬釋放二值信號量,最後進入 500ms 的阻塞狀態
  3. 當高優先順序任務進入阻塞狀態後,接下來會執行就緒狀態的中等優先順序任務 Task_Middle ,該任務無具體功能,僅僅通過串口輸出 “Task_Middle is running”,然後同樣進入 500ms 的阻塞狀態
  4. 由於高優先順序和中等優先順序任務都進入阻塞狀態,這時才輪到低優先順序任務 Task_Low 執行,低優先順序任務 Task_Low 成功獲取到二值信號量並通過串口輸出 “Task_Low take it” ,然後利用 500ms 的 HAL 庫延時函數模擬連續運行
  5. 在 Task_Low 連續運行期間,在其即將執行完第一個 HAL_Delay(500); 時,高優先順序任務 Task_High 從 500ms 的阻塞狀態恢復,然後嘗試獲取已經被 Task_Low 獲取的二值信號量,結果就是進入阻塞狀態等待 Task_Low 釋放二值信號量
  6. 緊接著 Task_Middle 從 500ms 的阻塞狀態恢復,通過串口輸出 “Task_Middle is running”,接著再次進入 500ms 阻塞狀態
  7. 由於高優先順序和中等優先順序任務再次進入阻塞狀態,因此調度器返回 Task_Low 被搶占時的程式處繼續執行,因此 Task_Low 通過串口輸出 “return Task_Low” ,然後利用第二個 HAL_Delay(500); 繼續模擬長時間運行
  8. 在 Task_Low 第二個 HAL_Delay(500); 即將執行完畢時,Task_Middle 再次從 500ms 的阻塞狀態恢復,通過串口輸出 “Task_Middle is running” ,然後再次進入 500ms 阻塞狀態(這裡 Task_High 由於不是因為延時進入的阻塞狀態所以未恢復運行狀態)
  9. 最後返回 Task_Low 任務,釋放二值信號量,一旦 Task_Low 任務釋放二值信號量,等待二值信號量的高優先順序任務 Task_High 會立馬退出阻塞狀態成功獲取到二值信號量,並會通過串口輸出 “Task_High get token“

從上述過程可知,從 Task_Low 獲取二值信號量之後到第一輪結束,Task_High 等待 Task_Low 釋放二值信號量,等待期間中等優先順序的任務 Task_Middle 卻先於高優先順序任務 Task_High 得到了執行,這就是所謂的優先順序翻轉問題,上述過程所述的實際串口輸出如下圖所示

4.5、互斥量的應用

首先在 STM32CubeMX 中單擊 Middleware and Software Packs/FREERTOS,在 Configuration 中單擊 Mutexes 選項卡,單擊 Add 按鈕增加互斥量 Mutex_PI ,具體如下圖所示

然後將上述實驗使用的所有二值信號量句柄 BinarySem_PIHandle 修改為互斥量 Mutex_PIHandle,不需要做其他任何操作,燒錄程式即可

打開串口助手,觀察串口助手的輸出,如下所述為第一輪詳細的任務執行流程

  1. 前4個步驟與 ”4.4、燒錄驗證“ 小節一致,只不過從二值信號量修改為互斥量
  2. 在第5步時,高優先順序任務 Task_High 從 500ms 的阻塞狀態恢復,輸出 ”Into Task_High“ ,然後嘗試獲取已經被 Task_Low 獲取的互斥量,結果就是進入阻塞狀態等待 Task_Low 釋放互斥量,同時將 Task_Low 的優先順序臨時提高到和高優先順序任務 Task_High 一樣的優先順序
  3. 緊接著 Task_Middle 從 500ms 的阻塞狀態恢復,但是由於現在 Task_Low 任務的優先順序要高於中等優先順序任務 Task_Middle ,因此不能搶占 Task_Low 任務,故無法執行任務體輸出 ”Task_Middle is running“ ,所以其狀態變為就緒狀態,它將等待所有高優先順序的任務執行完後才會執行
  4. 於是優先順序被臨時提高到高優先順序的任務 Task_Low 繼續執行其函數體內容,輸出 ”return Task_Low“ ,然後執行第二個 HAL_Delay(500); ,最後釋放互斥量,通過串口輸出 ”Task_Low give it“
  5. 一旦互斥量被 Task_Low 釋放,處於阻塞狀態的 Task_High 就會立馬恢復運行狀態獲取到互斥量,所以會通過串口輸出 ”Task_High get token“ 和 ”Task_High give token“ ,同時當互斥量被 Task_High 任務成功獲取之後,會將任務 Task_Low 臨時提高的優先順序恢復到其原來的低優先順序,最後 Task_High 調用延時函數進入 500ms 的阻塞狀態
  6. 當高優先順序任務 Task_High 進入阻塞狀態後,系統內現在剩餘就緒狀態的中等優先順序任務 Task_Middle 和 低優先順序任務 Task_Low ,所以輪到 Task_Middle 任務執行,其將通過串口輸出 ”Task_Middle is runing“ ,至此一輪結束

讀者可以自行對比將二值信號量更換為互斥量之後的串口輸出結果,可以發現在步驟4中,中等優先順序的任務 Task_Middle 不再先於高優先順序的任務 Task_High 得到執行,上述整個過程串口數據的完整輸出如下圖所示

5、註釋詳解

註釋1:圖片來源於 Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf

參考資料

STM32Cube高效開發教程(基礎篇)

Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 渲染系統概述 WPF 採用保留模式渲染系統 (Retained Mode Rendering System),該系統可分為 UI 線程和複合線程兩個主要部分,兩者協作完成 WPF 應用程式的渲染工作。 1.1 立即模式GUI和保持模式GUI 圖形 API 可分為保留模式API 和即時模式API ...
  • 在開發各種應用程式時,我們經常需要對文件系統中的文件或文件夾進行實時監測,以便在文件內容改變、文件被創建或刪除時能夠及時做出反應。在 C# 中,System.IO.FileSystemWatcher 類為我們提供了這樣一個強大的功能。 一、引入 FileSystemWatcher 類 首先,在項目中 ...
  • 我個人編寫的庫,在我個人網站,小程式等很多地方都在使用中,大家可以搜索小程式 什邡市宅貓君網路工作室 或者到我的網站 store.zhaimaojun.cn 去體驗支付和登錄效果。 本庫主要實現了native pay(二維碼支付)jsapi pay(小程式直接調起支付),需要註意的是這是基於api3 ...
  • 這是我自己個人編寫的日誌記錄,主要使用在只需要記錄日誌,偶爾到文件中查看一下日誌記錄的情況。我自己寫的一些服務之類的是使用了這個的,代碼很少,使用很簡單。 第一步 搜索和安裝我的Nuget包 搜索和安裝zmjtool這個包,我寫的,如下圖: 第二步 引入namespace和創建logger對象 1 ...
  • 支持.Net/.Net Core/.Net Framework,可以部署在Docker, Windows, Linux, Mac。 RabbitMQ作為一款主流的消息隊列工具早已廣受歡迎。相比於其它的MQ工具,RabbitMQ支持的語言更多、功能更完善。 1.發送消息、獲取消息、使用消息 本文提供一 ...
  • 前言 異常的處理在我們應用程式中是至關重要的,在 dotNet 中有很多異常處理的機制,比如MVC的異常篩選器, 管道中間件定義try catch捕獲異常處理亦或者第三方的解決方案Hellang.Middleware.ProblemDetails等。MVC異常篩選器不太靈活,對管道的部分異常捕獲不到 ...
  • 讓我先把相關的報錯信息通過文字貼到下方,方便被檢索出來 出錯了! (warning!) curl error code=403; 系統錯誤 (explorer.editor.fileGet) explorer/editor.class.php[64] IO::fileSubstr(0,1,2) bi ...
  • 通過視頻幀圖片提取,圖片批量裁剪,轉換為BMP文件並取模,獲得顯示屏代碼,基於STC32單片機,在8x8點陣LED模塊上實現動畫播放。 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...