1 回答

TA貢獻1807條經驗 獲得超9個贊
這是因為當您Task在調用Sleep()時返回運行時,即使您正在分配變量。
令人困惑的是,Task如果您在調用之前將其分配給變量(、 或)A,則不會開始B,但事實并非如此。一旦你分配給,就被調用了;因此in方法正在運行。將其分配給變量與否在調用方法時開始;因為在你啟動的方法中。 Cawait A; sleep();Asleep()Tasksleep()TaskTask
知道這一點;你打電話時:
await A;
await B;
await C;
A、B 和 C 已經同時開始……在等待 A 之后,很可能 B 和 C 也已完成或距離完成幾毫秒。
在某些情況下,您可以引用Task尚未啟動的a ,但您必須有意返回非運行Task才能執行此操作。
還要回答對您的問題的編輯。
Tasks 有一個被調用的方法GetAwaiter(),它返回一個TaskAwaiter. 在 C# 中,當您編寫時,您var task = sleep();將實際分配給Task任務變量。同樣,當你編寫await sleep();編譯器時,它會做一些很酷的事情,并且它實際上會調用Task.GetAwaiter()方法;這是訂閱的。在Task將運行并完成時,TaskAwaiter將觸發延續動作。這不能用簡單的答案來解釋,但了解外部邏輯會有所幫助。
除其他外,TaskAwaiter工具ICriticalNotifyCompletion又會執行INotifyCompletion。兩者都有一個方法,OnCompleted(Action)并且UnsafeOnCompleted(Action) (您可以通過命名約定猜測哪個是哪個)。
要注意的另一件事是Task.GetAwaiter()返回 aTaskAwaiter但Task<TResult>.GetAwaiter()返回 a TaskAwaiter<TResult>。兩者沒有太大區別,只是GetResult()兩個任務的方法有區別;這就是在編組回正確的線程上下文時調用的內容。在TaskAwaiter.GetResult()返回void和TaskAwaiter<TResult>.GetResult()回報TResult。
我覺得如果我進一步深入研究,我將不得不寫幾頁來詳細解釋這一切......希望只是解釋你的問題并稍微拉開帷幕就會有足夠的光線來幫助你理解和深入挖掘如果你更好奇。
好的,所以根據下面的評論,我想進一步描述我的答案。
我會從這個簡單的開始;讓我們做一個Task;一個沒有運行的,先看看它。
public Task GetTask()
{
var task = new Task(() => { /*some work to be done*/ });
//Now we have a reference to a non-running task.
return task;
}
我們現在可以調用如下代碼:
public async void DoWork()
{
await GetTask();
}
……但我們會永遠等待;直到應用程序結束,因為Task從未啟動。然而; 我們可以這樣做:
public async void DoWork()
{
var task = GetTask();
task.Start();
await task;
}
...它將等待運行Task并在Task完成后繼續。
知道了這一點,您可以根據需要進行任意數量的調用,GetTask()并且只會引用Task尚未啟動的 s。
在您的代碼中正好相反,這很好,因為這是最常用的方式。我鼓勵您確保您的方法名稱通知用戶您如何返回Task. 如果Task已經在運行,最常見的約定是方法名稱以 結尾Async。Task為了清楚起見,這是另一個通過跑步來做的例子。
public Task DoTaskAsync()
{
var task = Task.Run(() => { /*some work to be done*/ });
//Now we have a reference to a task that's already running.
return task;
}
現在我們很可能會像這樣調用這個方法:
public async void DoWork()
{
await DoTaskAsync();
}
然而; 請注意,如果我們只是想像Task我們之前所做的那樣引用,我們可以,唯一的區別是這Task是在先前沒有的地方運行。所以這個代碼是有效的。
public async void DoWork()
{
var task = DoTaskAsync();
await task;
}
最大的收獲是 C# 如何處理 async / await 關鍵字。 async告訴編譯器該方法將成為Task. 簡而言之; 編譯器知道查找所有await調用并將方法的其余部分放在延續中。
的await關鍵字告訴編譯器調用Task.GetAwaiter()上的方法Task(和基本上訂閱INotifyCompletion和ICriticalNotifyCompletion)轉換成信號的方法中的延續。
我想補充一點,以防萬一你不知道。如果您確實有多個任務要等待,但寧愿等待一項任務,就好像它們都是一項任務一樣,那么您可以使用Task.WhenAll()So 來代替:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await taskA;
await taskB;
await taskC;
你可以像這樣寫得更干凈一點:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await Task.WhenAll(taskA, taskB, taskC);
并且內置了更多的方法來做這種事情;只是探索它。
- 1 回答
- 0 關注
- 199 瀏覽
添加回答
舉報