首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 非同步程式設計全景:同步 vs 非同步、APM/EAP/TAP 三模式、I/O 密集型為何必須用非同步

     編輯:關於C#

非同步不是多執行緒的同義詞。**非同步 = 啟動作業 → 立即返回 → 完成時通知**,主執行緒不阻塞。I/O 密集型(檔案、網路、資料庫)非同步能釋放執行緒回執行緒池,配合 DMA(Direct Memory Access)幾乎零 CPU 佔用;計算密集型才適合多執行緒。本文系統梳理 .NET 三代非同步模式與實戰範例。

同步 vs 非同步:輸出對比


// 同步:主執行緒卡住
reader.SyncReadFile(path);
Console.WriteLine("我這裡還有一大灘事呢"); // 必須等讀完才印

// 非同步:主執行緒繼續跑
reader.AsyncReadFile(path);
Console.WriteLine("我這裡還有一大灘事呢"); // 立即印出
DoSomething(); // 繼續做別的事

同步輸出:循序阻塞

非同步輸出:交錯執行

.NET 三代非同步模式

模式年代關鍵 API特徵
APM (Asynchronous Programming Model)1.0BeginXxx/EndXxx, IAsyncResult手動管理狀態、易寫錯、回調地獄
EAP (Event-based Asynchronous Pattern)2.0XxxAsync, XxxCompleted event元件拖拉即用、WinForms/WebForms 友善
TAP (Task-based Asynchronous Pattern)4.0+Task/Task<T>, async/await現代標準、線性寫法、組合性強、編譯器生成狀態機

APM 範例:FileStream BeginRead/EndRead


public void AsyncReadFile(string path) {
    if (!File.Exists(path)) return;
    var fs = new FileStream(path, FileMode.Open); // 不能用 using,回調時需存取
    fs.BeginRead(Buffer, 0, BufferSize, AsyncReadCallback, fs);
}

private void AsyncReadCallback(IAsyncResult ar) {
    var fs = (FileStream)ar.AsyncState;
    fs.EndRead(ar); // 必須呼叫取得實際讀取位元組數
    fs.Close();
    string text = Encoding.UTF8.GetString(Buffer);
    Console.WriteLine("讀取完成:" + text);
}

EAP 範例:BackgroundWorker(上一篇已詳述)

  • DoWork(背景執行緒)、ProgressChanged(UI 執行緒)、RunWorkerCompleted(UI 執行緒)
  • 內建進度回報、取消、例外傳遞,適合 WinForms 快速開發。

委托 BeginInvoke/EndInvoke:自訂非同步最簡單


public delegate string DownloadDelegate(string url);

public IAsyncResult BeginDownload(string url) {
    var d = new DownloadDelegate(DownloadSync);
    return d.BeginInvoke(url, DownloadCallback, d); // 回調帶入 delegate 自身
}

private void DownloadCallback(IAsyncResult ar) {
    var d = (DownloadDelegate)ar.AsyncState;
    string result = d.EndInvoke(ar); // 取得回傳值
    Console.WriteLine("下載完成:" + result);
}

委托編譯後會自動生成 BeginInvoke/EndInvoke,無需手寫 IAsyncResult 實作。

TAP:async/await(現代首選)


public async Task DownloadAsync(string url) {
    using var client = new HttpClient();
    return await client.GetStringAsync(url); // 非同步等待,不阻塞
}

// 呼叫端
public async void Btn_Click(object sender, EventArgs e) {
    btn.Enabled = false;
    string html = await DownloadAsync("https://example.com");
    txtResult.Text = html;
    btn.Enabled = true;
}

I/O 密集型為何必須非同步?

  • 同步 I/O:執行緒**阻塞等待**硬體(磁碟旋轉、網卡收封包),白白佔用執行緒池資源。
  • 非同步 I/O:發出指令 → **釋放執行緒** → 硬體透過 DMA 直接寫入記憶體 → 完成中斷通知 → 執行緒池取走續作業。
  • 單一執行緒可同時發起成千上萬個非同步 I/O,吞吐量指數級提升。

避坑指南

  • 不要 在非同步方法中對 Task 用 .Result/.Wait() → 死結風險。
  • 不要 在 using 區塊內啟動 APM 非同步(回調時資源已釋放)。
  • 記得 async void 僅用於事件處理器,其他一律 async Task。
  • 配置 ConfigureAwait(false) 避免捕獲 UI 同步上下文(類庫代碼必加)。

從 APM 到 TAP,.NET 非同步演進史就是「如何讓開發者寫出正確、可讀、可組合的非同步代碼」的歷史。新專案請直接拥抱 async/await + Task,舊代碼逐步遷移。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved