C# 非同步程式設計

更新於 發佈於 閱讀時間約 9 分鐘

在 C# 中,非同步程式設計是一種有效提升應用性能、併發處理能力的重要技術。今天,我們將詳細介紹 C# 中的非同步核心概念:asyncawaitTaskTask<T>,說明它們的使用方式,並探討一些進階應用。最後,我們會簡單介紹一些非同步程式設計中常見的問題。


什麼是非同步程式設計?

非同步程式設計允許應用程式在處理長時間的操作(例如 I/O 操作或網絡請求)時,釋放執行緒資源來處理其他任務。這樣可以有效地提升應用性能,特別是在高併發的情況下,非同步程式設計讓伺服器可以同時處理更多的請求。


核心關鍵字與類型介紹

在 C# 中,非同步程式設計的核心由以下四個要素構成:

  • async
  • await
  • Task
  • Task<T>

這些工具使得我們可以有效地進行非同步操作,同時保持代碼的簡潔和易讀性。


1. async 關鍵字

async 是用來標記一個方法為非同步的關鍵字,允許你在方法內部使用 await 關鍵字來等待非同步操作完成。需要注意的是,async 本身不會讓一個方法變得非同步,它只是一個標記,允許該方法內部執行非同步操作。

語法與使用

public async Task<string> FetchDataAsync()
{
// 非同步操作
await Task.Delay(1000); // 模擬網路請求的延遲
return "Data fetched";
}

  • async 方法的返回類型:通常是 TaskTask<T>。如果不返回任何結果,可以使用 Task;如果返回結果,則使用 Task<T>

2. await 關鍵字

await 關鍵字用於等待一個非同步操作完成,並且在操作完成後繼續執行方法的剩餘部分。當一個方法執行到 await 時,執行緒將被釋放,允許其他任務併發執行。

語法與使用

public async Task<string> GetDataAsync()
{
// 等待非同步操作完成
var data = await FetchDataFromApiAsync();
return data;
}

  • await 的作用:防止應用程式阻塞,讓操作在後台完成,並保持代碼邏輯的清晰度和可讀性。

3. Task 類型

Task 是 C# 中表示非同步操作的基本類型。它代表一個未來會完成的操作,這個操作不會返回結果。Task 可以用於表示僅執行任務但不需要返回值的非同步方法。

語法與使用

public async Task PerformActionAsync()
{
await Task.Delay(1000); // 模擬非同步操作
}


4. Task<T> 類型

Task<T>Task 的泛型版本,它用來表示會返回結果的非同步操作。T 代表操作完成後返回的數據類型。

語法與使用

public async Task<string> FetchDataAsync()
{
await Task.Delay(1000); // 模擬非同步操作
return "Data fetched"; // 返回操作結果
}

  • Task<T> 的常見應用:例如,當從資料庫或 API 獲取數據時,通常會使用 Task<T>

進階應用:並行與性能優化


除了基本的 asyncawait,C# 提供了許多進階的非同步處理工具,這些工具在實現性能優化和更高併發度時非常有用。了解這些進階技術,可以幫助你更靈活地應用非同步程式設計並避免常見陷阱。


使用 Task.WhenAll 進行真正的並行處理

原理介紹

Task.WhenAll 是一個非常強大的工具,用於並行執行多個非同步任務。當你有多個彼此獨立的非同步操作時,Task.WhenAll 可以將這些操作同時進行,而不是按順序等待每個操作完成。它返回一個 Task,該 Task 在所有內部任務都完成時才會完成。這意味著它不會等待每個任務一個接一個地完成,而是讓它們同時執行,從而節省了時間。

應用場景

一個經典的應用場景是在處理多個 API 請求或同時進行多個 I/O 操作時。舉例來說,如果你的應用需要從多個來源下載數據,這些下載操作之間沒有相互依賴性,那麼使用 Task.WhenAll 可以顯著提高性能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class Program
{
public static async Task Main(string[] args)
{
// 呼叫 FetchDataFromMultipleSourcesAsync 並取得結果
var urls = new List<string>
{
"<https://example.com/data1>",
"<https://example.com/data2>",
"<https://example.com/data3>"
};

var results = await FetchDataFromMultipleSourcesAsync(urls);

// 輸出每個請求的結果
foreach (var result in results)
{
Console.WriteLine(result);
}
}

public static async Task<IEnumerable<string>> FetchDataFromMultipleSourcesAsync(List<string> urls)
{
// 創建一個包含多個 Task 的集合,透過 HttpClient 發送非同步請求
var tasks = urls.Select(url => FetchDataAsync(url));

// 使用 Task.WhenAll 等待所有請求完成
var results = await Task.WhenAll(tasks);

return results;
}

// 模擬非同步的資料請求
public static async Task<string> FetchDataAsync(string url)
{
await Task.Delay(1000); // 模擬非同步延遲
return $"資料從 {url} 取得";
}
}


非同步程式設計的挑戰

在設計非同步程式時,開發者往往會遇到一些常見的挑戰。這些挑戰可能來自於非同步的特性,或是與系統架構相關的問題。以下是幾個常見的問題:

  • 資源競爭與併發控制:當多個非同步操作同時訪問共享資源時,可能會發生資源競爭,導致數據不一致。
  • 錯誤處理與異常恢復:在處理大量非同步操作時,如何有效地捕捉並處理錯誤成為一大挑戰,特別是在涉及網路或外部資源時。
  • 併發控制與限流:在高併發環境下,如果無法有效控制非同步任務的數量,可能會導致系統資源耗盡。
  • 非同步死鎖:不當的同步操作和非同步操作混合使用,可能導致應用程式出現死鎖,這是非同步設計中一個難以診斷的問題。

每日小結

在這篇文章中,我們詳細介紹了 C# 中的非同步程式設計,包括 asyncawaitTaskTask<T> 的基本使用方法和最佳實踐。我們還探討了一些進階應用,如如何使用 Task.WhenAll 並行處理非同步任務。

最後,我舉出幾個非同步程式設計常預見的問題,如資源競爭、錯誤處理、併發控制等。在下一篇文章中,我們將深入探討這些問題,並介紹具體的解決方案,幫助你在高併發和複雜環境中構建更加穩定和高效的非同步應用程式。並且會更完整介紹非同步API的設計架構。

avatar-img
0會員
12內容數
歡迎來到 ChiYu Code Journey!這裡是我分享技術心得與開發經驗的空間,主要內容涵蓋 C#、.Net、API 開發及雲端等程式主題。偶爾也會分享一些日常生活點滴,像是我與我家可愛的法鬥相處的趣事等。希望在這裡能和大家一起學習、交流,一同踏上這段程式旅程!
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
ChiYu Code Journey 的其他內容
這篇文章探討非同步編程的優缺點,並提供在設計非同步系統時需要注意的事項。非同步編程允許程式在等待 I/O 操作完成的同時,繼續執行其他工作,提高響應速度和資源利用率。然而,非同步程式設計也增加了系統複雜性,需要謹慎處理錯誤和確保代碼可讀性。
RESTful API 設計原則與實務,包含資源導向設計、HTTP 方法使用、狀態碼應用、無狀態性、分頁排序、版本控制、避免過度設計等面向,並輔以優良與不良設計範例說明,最後以每日小結歸納重點。
本文探討三種主流 API 架構設計:REST、gRPC 和 GraphQL,比較它們的優缺點及適用場景,並提供 SEO 建議,以提升文章的搜尋引擎排名。
本文詳細闡述如何建立強大的API模型,涵蓋API需求分析、資源定義、資源層級架構、操作事件設計、流程補充,以及時序圖驗證等步驟,旨在確保API滿足業務需求並具備良好的可維護性和擴展性。文章以圖書館管理系統為例,逐步說明API建模流程,並解釋HTTP方法和API安全特性在API設計中的重要性。
本文探討程式設計下的API模型設計,特別是Resource-Based API的概念,強調區分「資料」與「資源」的重要性,避免直接暴露資料庫原始資料。文中介紹MVC和MVVM架構,說明如何透過模型過濾、轉換資料,保護敏感數據並提升API靈活性及可維護性。
本文討論 API 設計中界定 API 邊界的重要性,說明如何避免多合一 API 的缺點,並透過理解業務需求、識別核心資源和劃分功能責任等步驟,設計出清晰、高效且易於維護的 API。文章以圖書館管理系統為例,說明如何界定 API 邊界,並說明正確使用 HTTP 方法和狀態碼的重要性。
這篇文章探討非同步編程的優缺點,並提供在設計非同步系統時需要注意的事項。非同步編程允許程式在等待 I/O 操作完成的同時,繼續執行其他工作,提高響應速度和資源利用率。然而,非同步程式設計也增加了系統複雜性,需要謹慎處理錯誤和確保代碼可讀性。
RESTful API 設計原則與實務,包含資源導向設計、HTTP 方法使用、狀態碼應用、無狀態性、分頁排序、版本控制、避免過度設計等面向,並輔以優良與不良設計範例說明,最後以每日小結歸納重點。
本文探討三種主流 API 架構設計:REST、gRPC 和 GraphQL,比較它們的優缺點及適用場景,並提供 SEO 建議,以提升文章的搜尋引擎排名。
本文詳細闡述如何建立強大的API模型,涵蓋API需求分析、資源定義、資源層級架構、操作事件設計、流程補充,以及時序圖驗證等步驟,旨在確保API滿足業務需求並具備良好的可維護性和擴展性。文章以圖書館管理系統為例,逐步說明API建模流程,並解釋HTTP方法和API安全特性在API設計中的重要性。
本文探討程式設計下的API模型設計,特別是Resource-Based API的概念,強調區分「資料」與「資源」的重要性,避免直接暴露資料庫原始資料。文中介紹MVC和MVVM架構,說明如何透過模型過濾、轉換資料,保護敏感數據並提升API靈活性及可維護性。
本文討論 API 設計中界定 API 邊界的重要性,說明如何避免多合一 API 的缺點,並透過理解業務需求、識別核心資源和劃分功能責任等步驟,設計出清晰、高效且易於維護的 API。文章以圖書館管理系統為例,說明如何界定 API 邊界,並說明正確使用 HTTP 方法和狀態碼的重要性。
你可能也想看
Google News 追蹤
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
分享一個有趣的套件,名為 await-to-js。 可以讓 Promise 與 await 的寫法更簡潔。
Thumbnail
簡要說明 JavaScript 的 Event Loop JavaScript 是單執行緒 (single-threaded) 語言,這意味著它一次只能執行一件事,因此所有函式都需要排隊等待執行,這被稱為同步 (synchronous)。在同步操作中,若函式過多或過於複雜,會導致程式阻塞 (blo
※ 非同步概念總複習 為什麼要使用 Promise? 在 JavaScript 開發中,處理非同步操作是常見需求,涉及如文件讀寫、數據庫查詢或網路請求等耗時任務。傳統的回調方式可能導致代碼結構混亂,稱為「回調地獄」,難以維護和理解。 Promise 是解決這問題的方法。它是一個物件(objec
認識 async/await基本概念: async 的本質是 promise 的語法糖 ,只要 function 標記為 async,就表示裡頭可以撰寫 await 的同步語法,而 await 顧名思義就是「等待」,它會確保一個 promise 物件都解決 ( resolve ) 或出錯 ( re
※ Promise基本介紹 什麼是 Promise? Promise 是 JavaScript 的一個構造函式,用於創建表示非同步操作的物件實例。使用 new Promise() 時,你會創建一個包含非同步操作的實例,這個實例可以透過其繼承的方法如 then(), catch(), 和 fina
※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
Thumbnail
程式與頻率時間 看起來這個問題有些奇怪,程式與頻率時間有什麼關係呢?一旦程式完成,似乎就不需要再理會頻率和時間了。實際上,這可能是一些不熟悉程式設計的人所提出的疑問。了解程式設計最重要的一點是,頻率和時間的安排會直接影響程式的效能和展現速度。 時間的利用 舉例來說,假設一個表單的每筆處理時間為
Thumbnail
非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
Thumbnail
為什麼需要非同步? 我們在「【Web微知識系列】 Web Workers」有介紹到在瀏覽器可執行腳本Javascript環境底下如何完成非同步的操作, 主要是為了讓任務更有效率的進行, 不會因為一個非常耗時的工作堵塞住整個服務, 導致無法服務他人的窘境。 大家應該經常在餐廳裡會看到服務員協
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
分享一個有趣的套件,名為 await-to-js。 可以讓 Promise 與 await 的寫法更簡潔。
Thumbnail
簡要說明 JavaScript 的 Event Loop JavaScript 是單執行緒 (single-threaded) 語言,這意味著它一次只能執行一件事,因此所有函式都需要排隊等待執行,這被稱為同步 (synchronous)。在同步操作中,若函式過多或過於複雜,會導致程式阻塞 (blo
※ 非同步概念總複習 為什麼要使用 Promise? 在 JavaScript 開發中,處理非同步操作是常見需求,涉及如文件讀寫、數據庫查詢或網路請求等耗時任務。傳統的回調方式可能導致代碼結構混亂,稱為「回調地獄」,難以維護和理解。 Promise 是解決這問題的方法。它是一個物件(objec
認識 async/await基本概念: async 的本質是 promise 的語法糖 ,只要 function 標記為 async,就表示裡頭可以撰寫 await 的同步語法,而 await 顧名思義就是「等待」,它會確保一個 promise 物件都解決 ( resolve ) 或出錯 ( re
※ Promise基本介紹 什麼是 Promise? Promise 是 JavaScript 的一個構造函式,用於創建表示非同步操作的物件實例。使用 new Promise() 時,你會創建一個包含非同步操作的實例,這個實例可以透過其繼承的方法如 then(), catch(), 和 fina
※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
Thumbnail
程式與頻率時間 看起來這個問題有些奇怪,程式與頻率時間有什麼關係呢?一旦程式完成,似乎就不需要再理會頻率和時間了。實際上,這可能是一些不熟悉程式設計的人所提出的疑問。了解程式設計最重要的一點是,頻率和時間的安排會直接影響程式的效能和展現速度。 時間的利用 舉例來說,假設一個表單的每筆處理時間為
Thumbnail
非同步程式設計(Asynchronous programming) 或是簡單的稱之為 async,它是一種並發程式模型(concurrent programming model),其目的就是讓多個任務能同時在作業系統的執行緒上執行,並透過 async/.await 保留同步。
Thumbnail
為什麼需要非同步? 我們在「【Web微知識系列】 Web Workers」有介紹到在瀏覽器可執行腳本Javascript環境底下如何完成非同步的操作, 主要是為了讓任務更有效率的進行, 不會因為一個非常耗時的工作堵塞住整個服務, 導致無法服務他人的窘境。 大家應該經常在餐廳裡會看到服務員協