本文還處於草稿階段,不免還有錯誤修改改正,邏輯還不是很清晰,筆者會努力完善,長期更新!html
標題起得有些"大",意在集你們的力量,總結出來一份關於Task相對"正確"的知識總結,歡迎讀者提出寶貴意見!本文內容來自於筆者在編碼的時候種種疑問,來自於對異步編程在操做系統中實際運行過程的好奇。平時使用Task戰戰兢兢,既想提升效率,又怕它不受控制,處處亂來。與其這樣,不如此時此刻一塊兒來了解它神祕的面紗吧!Just do IT.git
新型應用普遍使用文件和網絡 I/O。 默認狀況下 I/O API 通常會阻塞,致使糟糕的用戶體驗和硬件利用率,除非但願學習和使用富有挑戰的模式。 基於任務的異步 API 和語言級異步編程模型改變了這種模型,只需瞭解幾個新概念就可默認進行異步執行。github
異步代碼具備如下特色:編程
來源: https://docs.microsoft.comc#
.NET 提供了執行異步操做的三種模式:服務器
基於任務的異步模式 (TAP) ,該模式使用單一方法表示異步操做的開始和完成。 TAP 是在 .NET Framework 4 中引入的。 這是在 .NET 中進行異步編程的推薦方法。 C# 中的 async 和 await 關鍵詞以及 Visual Basic 中的 Async 和 Await 運算符爲 TAP 添加了語言支持。 有關詳細信息,請參閱基於任務的異步模式 (TAP)。網絡
基於事件的異步模式 (EAP) ,是提供異步行爲的基於事件的舊模型。 這種模式須要後綴爲 Async
的方法,以及一個或多個事件、事件處理程序委託類型和 EventArg
派生類型。 EAP 是在 .NET Framework 2.0 中引入的。 建議新開發中再也不使用這種模式。 有關詳細信息,請參閱基於事件的異步模式 (EAP)。多線程
異步編程模型 (APM) 模式(也稱爲 IAsyncResult 模式),這是使用 IAsyncResult 接口提供異步行爲的舊模型。 在這種模式下,同步操做須要 Begin
和 End
方法(例如,BeginWrite
和 EndWrite
以實現異步寫入操做)。 不建議新的開發使用此模式。 有關詳細信息,請參閱異步編程模型 (APM)。異步
爲了快速比較這三種模式的異步操做方式,請考慮使用從指定偏移量處起將指定量數據讀取到提供的緩衝區中的Read
方法:async
public class MyClass { public int Read(byte [] buffer, int offset, int count); }
此方法對應的 TAP 將公開如下單個 ReadAsync
方法:
public class MyClass { public Task<int> ReadAsync(byte [] buffer, int offset, int count); }
對應的 EAP 將公開如下類型和成員的集:
public class MyClass { public void ReadAsync(byte [] buffer, int offset, int count); public event ReadCompletedEventHandler ReadCompleted; }
對應的 APM 將公開 BeginRead
和 EndRead
方法:
public class MyClass { public IAsyncResult BeginRead( byte [] buffer, int offset, int count, AsyncCallback callback, object state); public int EndRead(IAsyncResult asyncResult); }
來源: https://docs.microsoft.com
請一路Async,不然會不可控。
網絡請求,文件讀寫時系統自帶的Async方法不會建立多線程,而是使用完成端口,依靠中斷來實現!
線程池中的線程分爲 WorkerThread 和 CompletionPortThread .
平時咱們使用的線程是WorkerThread,IO讀寫使用的是CompletionPortThread
如下代碼不會建立多個線程(WorkerThread),代碼會在當前線程工做,且不會堵塞哦。
執行起來很是相似同步程序, 使用 await RunActionAsync(()=>{});
後,會當即執行程序
public Task RunActionAsync(Action action) { TaskCompletionSource<Task> source = new TaskCompletionSource<Task>(TaskCreationOptions.AttachedToParent); Task<Task> task = source.Task; try { action.Invoke(); } catch (Exception ex) { source.SetException(ex); } source.SetResult(Task.CompletedTask); return task; }
如下代碼會建立新線程(WorkerThread),位於線程池,線程池默認最小WorkerThread爲CPU核心數,CompletionPortThread爲1000(實際最小值依實際運行狀況而定,可手工修改)
運行時並不會當即執行Action,按照默認執行計劃(TaskScheduler.Default執行,好比用for循環一堆Task.Run(async ()=> {await httpgetAsync(); echo(i); )任務,執行時你會發現i都是最後一個值
await Task.Run(()=>{});
如下代碼會建立新線程(WorkerThread),在不在ThreadPool關鍵在於TaskCreationOptions枚舉,若是爲LongRunning,則直接會建立一個非線程池的線程執行任務,若是不是,則會在線程池裏尋找線程,若是沒有,會在線程池裏新申請線程(建立一個耗時一秒),執行任務。
會當即執行Action
Task.Factory.StartNew(_ => { action.Invoke(); }, null, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)
如下代碼.NET Core不支持哦,請使用Task.Factory.StartNew代替
Task.Factory.FromAsync( new Func<AsyncCallback, object, IAsyncResult>((cb, obj) => action.BeginInvoke(biz, cb, obj)), new Action<IAsyncResult>(ar => action.EndInvoke(ar)), null)
The danger of TaskCompletionSource class
瞭解 .NET 的默認 TaskScheduler 和線程池(ThreadPool)設置,避免讓 Task.Run 的性能急劇下降
.NET 中當心嵌套等待的 Task,它可能會耗盡你線程池的現有資源,出現相似死鎖的狀況
.NET 中使用 TaskCompletionSource 做爲線程同步互斥或異步操做的事件
定義一組抽象的 Awaiter 的實現接口,你下次寫本身的 await 可等待對象時將更加方便
.NET 除了用 Task 以外,如何本身寫一個能夠 await 的對象?
Asynchronous I/O in C#: I/O Completion Ports
Asynchronous I/O in C#: I/O Completion Ports
Migrating Delegate.BeginInvoke Calls for .NET Core
本文采用知識共享署名-非商業性使用-相同方式共享 2.5 中國大陸許可協議進行許可,發表在CSDN和博客園,歡迎讀者轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接!請讀者/爬蟲們尊重版權