C#異步Task
概述
異步是什麼?爲什麼需要異步?
- 使用多線程的目的其實即使爲了實現異步+並行,異步:是相對同步的,同步就是一個流程安裝一個流程執行完畢,異步就是在不影響主流程的執行同時,可以執行其他流程,這也就是達到了幾個邏輯並行執行的效果。
- 異步編程要解決的問題就是許多耗時的IO可能會阻塞線程導致CPU空轉降低效率,或者一個長時間的後臺任務會阻塞用戶界面。通過將耗時任務異步執行來使系統有更高的吞吐量,或保持界面的響應能力。
- 同步/異步/阻塞/非阻塞/BIO/NIO/AIO 參考
https://www.cnblogs.com/lixinjie/p/a-post-about-io-clearly.html - C# 中實現異步的方式有很多種,本文主要講解常用方案Task
基本語法
1. Task創建執行
public static void TaskCreateRun()
{
// 方式1: Task.Run
Task.Run(() =>
{
MessageBox.Show("Task.Run");
});
// 方式2: Task.Factory.StartNew
Task.Factory.StartNew(() =>
{
MessageBox.Show("Task.Factory.StartNew");
});
//方式3:new Task<返回類型>
Task<int> t=new Task<int>(() => { MessageBox.Show("new Task"); return 1;});
t.Start();
}
2. Task異步方法
- 使用async關鍵字標記,表示該方法可以包含異步操作。
- 通常返回Task或Task
類型,其中T是返回值類型。 - 使用await或Result關鍵字暫停方法執行,直到等待的異步操作完成。
- 使用await調用異步方法,方法定義必須添加async
方法定義:通常返回Task或Task
public static async Task<string> GetStringAsync()
{
//方式1: 使用await的方法,方法定義必須添加async
//return await Task.Run(() =>
//{
// return "Task.FromResult 返回結果";
//});
//方式2: 使用Task.FromResult
return await Task.FromResult("GetStringAsync Task.FromResult 返回結果");
}
public static Task<string> GetStringAsync2()
{
return Task.FromResult("GetStringAsync2 Task.FromResult 返回結果");
}
方法調用:使用await或result關鍵字暫停方法執行,獲取結果。 暫停多個異步方法 WaitAll、WaitAny、WhenAll。
var r= GetStringAsync().Result;
MessageBox.Show(await GetStringAsync2());
GetStringAsync2().Wait();//同步,獲取不到返回值,只適合無返回值的方法
//處理多個異步方法
Task.WaitAll(GetStringAsync2(),GetStringAsync());//同步阻塞,所有執行完成。獲取不到返回值,只適合無返回值的方法。
Task.WaitAny(GetStringAsync2(),GetStringAsync());//同步阻塞,其中1個異步執行完成。獲取不到返回值,只適合無返回值的方法。
//同步阻塞,獲取到所有異步方法的返回值
Task.WhenAll(GetStringAsync2(), GetStringAsync()).ContinueWith(p => {
p.Result.ToList().ForEach(t => MessageBox.Show(t));
});
注意事項
- Task.Wait()和Task.Result 將異步轉爲同步,容易造成死鎖。儘量少使用。
- 調用Task.WaitAll的時候,會阻塞當前線程,直到所有的Task都完成了。而Task.WhenAll方法不會阻塞當前線程,而是直接返回了一個Task,只有在讀取這個Task的Result的時候,纔會引起阻塞。
3. 取消異步執行CancellationTokenSource
static CancellationTokenSource cts = new CancellationTokenSource();
static CancellationToken token = cts.Token;
/// <summary>
/// 取消異步方法
/// </summary>
public static void TaskCanceled()
{
Task task = Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
if (token.IsCancellationRequested)
{
MessageBox.Show("任務已取消");
return;
}
MessageBox.Show($"正在執行操作:{i}");
Thread.Sleep(300);
}
MessageBox.Show("任務完成");
}, token);
}
//調用
TaskCanceled();
Thread.Sleep(3000);
cts.Cancel();
4.Task並行數量控制LimitedConcurrencyLevelTaskScheduler
-
https://www.cnblogs.com/DiscoverPalace/archive/2015/06/10/4567222.html
-
https://msdn.microsoft.com/zh-cn/library/system.threading.tasks.taskscheduler(v=vs.110).aspx
常見的應用場景
- 耗時IO或三方服務:文件下載、日誌記錄、消息訂閱、郵件短信發送等。
- Web服務器: Web 服務器通常使用多線程來處理多個客戶端的請求。每個請求都由一個單獨的線程處理,這樣可以避免單個請求阻塞其他請求。
- 媒體播放器: 媒體播放器通常使用多線程來解碼音頻和視頻數據。解碼是一項耗時的操作,如果使用單線程進行解碼,則會阻塞播放。使用多線程可以將解碼和播放分開進行,從而提高播放的流暢性。
- 遊戲: 遊戲通常使用多線程來渲染圖形、處理玩家輸入和更新遊戲狀態。這些任務都需要大量的 CPU 時間,使用多線程可以充分利用多核 CPU 的優勢,從而提高遊戲的性能。
- 科學計算: 科學計算通常需要處理大量的數據,可以使用多線程來將計算任務分解成多個子任務,同時執行。這可以顯著提高計算效率。
總結
Task是C#中一種功能強大且易於使用的異步編程工具,它可以幫助我們開發更加響應、高效和易於維護的應用程序。
-
1、Task的創建運行可以有三種方式:new Task/Task.Factory/Task.Run
-
2、Task的返回參數定義Task<返回類型> 。獲取返回值:Task.Result->要阻塞主流程
-
3、Task線程的同步實現不僅僅可以通過RunSynchronously來實現同步運行,當然還可以通過Task.Result/Task.Wait等方式來變向實現
-
4、Task的wait/waitAll/waitAny實現阻塞等待執行結果
-
5、Task的WhenAny、WhenAll、ContinueWith實現延續操作
-
6、CancellationTokenSource實現異步任務取消
-
7、異步方法之:(async/await)實現同步和異步調用等