[英]How is a task with await executed with no additional threads
這是該問題的后續問題:
如果async-await沒有創建任何其他線程,那么它如何使應用程序響應?
和這篇博客文章:
http://blog.stephencleary.com/2013/11/there-is-no-thread.html
關於鏈接問題的可接受答案。他描述了調用await
時發生的步驟。
他在第3和第4步中寫道,當發生這種情況時,當前線程將返回到調用上下文,從而使消息循環可用於接收其他消息,從而使應用程序做出響應。 然后在第5步中,他寫道,我們調用await的任務現在已完成,並且原始方法的其余部分在收到新消息以繼續該方法的其余部分之后繼續進行。
我不了解的是,如果沒有新線程,並且原始線程正忙於調用上下文,那么從頭開始如何精確執行該任務?
因此,我轉到了博客文章,該文章描述了整個操作實際上是如何在低得多的層次上完成的(老實說,我真的不知道它們是如何工作的),然后只是一個通知它已經完成了...但是我不明白為什么在執行此任務時突然想到,您可以依靠其他硬件來進行計算,而不是依靠CPU,從而以某種方式使得不創建新線程成為可能。如果此任務是一些復雜的計算,那該怎么辦?我們不需要CPU嗎? 然后我們回到我在寫的內容,即當前線程已經在忙於調用上下文了……
但是我不明白為什么在執行此任務時突然可以依靠其他硬件來進行計算,而不是依靠CPU來以某種方式使不創建新線程成為可能。
因為CPU不僅是硬件設備集中並行性的來源。
線程在邏輯上與CPU相關,因此對於任何與CPU綁定的工作負載,我們要么通過創建線程來顯式使用線程,要么使用更高級別的機制(例如線程池或Task.Run
或隱式使用線程-每個應用程序無論如何都在某個默認線程內啟動。
但是還有另一種操作-輸入/輸出操作,這意味着使用CPU-RAM硬件設備以外的其他設備,例如磁盤,網絡適配器,鍵盤,各種外圍設備等。此類設備處理傳入和傳出的數據。異步-沒有人知道您下次何時按下鍵或網絡中有新數據到達。 為了處理異步,這樣的硬件設備能夠在不占用CPU的情況下傳輸數據(簡單地說,設備在RAM所在的地址中提供了一些地址,然后數據可以自己進行傳輸)。 這是一個非常簡單的描述,但是您可以認為大多數異步流都以它結尾。 如您所見,此處不需要CPU,因此無需創建新線程。 至於入站數據,該機制非常相似,唯一的區別是,一旦數據到達,它將被放入特定的RAM區域以供進一步使用。 當設備完成數據轉換(入站或出站)時,它將發出稱為中斷的特定信號,以通知CPU操作完成,並且CPU對中斷做出反應,並觸發通常位於硬件設備驅動程序中的特定代碼執行-這樣,驅動程序可以將通知發送到更高級別。 中斷可能來自異步設備,CPU必須暫停當前正在執行的任何當前執行,並切換到中斷處理程序。 當設備驅動程序正在執行中斷處理程序時,它會將有關I / O完成的通知發送到更高級別的OS堆棧,最后,該通知將觸發啟動I / O操作的應用程序。 如何完成主要取決於應用程序所運行的操作系統。 對於Windows,有一種稱為I / O完成端口的特定機制,該機制意味着使用某些線程池來處理I / O完成通知。 這些通知最終從CLR發送到應用程序,並觸發繼續執行,最終可以在單獨的線程上運行,該線程可以是I / O線程池中的線程,也可以是任何其他線程,具體取決於特定的等待者實現。
至於您所引用的文章的總體思路,可以用以下措辭來表述: async/await
后面沒有線程,除非您明確創建它,因為await/async
本身只是高級通知框架,可以擴展與下面的任何異步機制一起使用。
我不了解的是,如果沒有新線程,並且原始線程正忙於調用上下文,那么從頭開始如何精確執行該任務?
請務必注意, 任務有兩種類型:我稱為委托任務和承諾任務 。 委派任務是並行處理中使用的任務類型-原始的任務並行庫使用方式,其中任務表示要執行的一定數量的代碼。
另一方面,Promise Tasks僅提供已完成某事的通知(以及該操作的結果/異常)。 Promise Tasks不執行代碼。 它們是回調的對象表示。 async
總是使用Promise Tasks。
因此,當async
方法返回Task
,該任務就是Promise Task。 它不會“執行”,但可以“完成” 。
我認為您需要了解使async
/ await
代碼工作的編譯器的級別。
采取這種方法:
public async Task<int> GetValue()
{
await Task.Delay(TimeSpan.FromSeconds(1.0));
return 42;
}
編譯后,您會得到:
[AsyncStateMachine(typeof(<GetValue>d__1))]
public Task<int> GetValue()
{
<GetValue>d__1 stateMachine = default(<GetValue>d__1);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <GetValue>d__1 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder<int> <>t__builder;
private TaskAwaiter <>u__1;
private void MoveNext()
{
int num = <>1__state;
int result;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
awaiter = Task.Delay(TimeSpan.FromSeconds(1.0)).GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter);
num = (<>1__state = -1);
}
awaiter.GetResult();
result = 42;
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<>t__builder.SetResult(result);
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
this.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
<>t__builder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
this.SetStateMachine(stateMachine);
}
}
正是IAsyncStateMachine.MoveNext()
允許允許調用代碼在到達await
時退出。 注意return;
在if (!awaiter.IsCompleted)
。
這只是一台狀態機,可以完成所有的工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.