我已經搜索了很多圍繞這個主題的資訊,并且我了解以下一般前提:
- 等待是從被呼叫者支持者到呼叫者的控制權移交
- 大多數現代 I/O 不在底層架構中使用真正的執行緒
- 大多數異步方法不會顯式啟動自己的執行緒(即 Web 請求)
特別是最后一個專案符號是我想討論的。為了將來證明這一點,讓我們用一個例子作為解釋的媒介。假設這是代碼塊:
public async Task<int> LongOperationWithAnInt32ResultAsync(string input)
{
/// Section A
_ = int.TryParse(input, out var parsedInt)
parsedInt = SyncOperationWithAnInt32Result(parsedInt);
/// Section B
await MyCustomTaskThatIWantAwaited();
/// Section C
return parsedInt;
}
private Task MyCustomTaskThatIWantAwaited()
{
/// Section D
AnotherSyncOperationWithVoidResult();
/// Section E
return Task.CompletedTask;
}
LongOperationWithAnInt32ResultAsync(string) 方法將同步執行,即使這不是預期的效果。
這是因為當呼叫者在 B 節進入被呼叫者時,來自 D 節和 E 節的代碼會立即執行,不會等待。如果洗掉部分 D 并且部分 E 改為“回傳 Task.Run(() => AnotherSyncOperationWithVoidResult())”,則會更改此行為。在這個新的 E 節中,被跟蹤的等待成為來自 Task.Run 的執行緒(與回傳的任務一起包裝)。
如果將 B 節替換為“等待 Task.Delay(10000);” 或“等待 FunctionalWebRequestAsync();” 它按預期作業。但是,據我所知,這些都不會在內部生成要遵循的執行緒——那么究竟在等待什么?
我接受了主要答案,因為它確實幫助我理解了我對任務功能的誤解,但也請參考我的答案。這可能是您正在尋找的東西。
uj5u.com熱心網友回復:
那么究竟在等待什么呢?
什么都沒有等待。Await 表示異步等待。對于異步等待,可等待物件 (the Task) 不應在await此時完成。在您的情況下, awaitable 已經完成(回傳的IsCompleted屬性),因此異步狀態機立即獲取其結果并像往常一樣繼續下一行。沒有理由打包機器的當前狀態,呼叫等待者的方法,并將不完整的回傳給呼叫者。TaskAwaitertrueOnCompletedTask
如果您想將異步方法的特定部分卸載ThreadPool到Task.Run. 例子:
public async Task<int> LongOperationWithAnInt32ResultAsync(string input)
{
/// Section A
_ = int.TryParse(input, out var parsedInt)
parsedInt = await Task.Run(() => SyncOperationWithAnInt32Result(parsedInt));
/// Section B
await Task.Run(async () => await MyCustomTaskThatIWantAwaited());
/// Section C
return parsedInt;
}
如果您喜歡以命令方式控制運行代碼的執行緒的想法,Microsoft.VisualStudio.Threading包SwitchTo中提供了一個擴展方法。使用示例:
await TaskScheduler.Default.SwitchTo(); // Switch to the ThreadPool
專家的意見是避免這種做法,堅持使用Task.Run.
uj5u.com熱心網友回復:
非常感謝@TheodorZoulias 的解釋和回答,因為這對我達到這一點至關重要。
我的誤解很簡單,Task(或 Task{T})不能用作委托。Task 是一個徹頭徹尾的類,這意味著如果你定義這個:
public Task DoSomeReallyLongWork()
{
SyncTaskThatIsReallyLong();
AnotherSyncTaskThatIsReallyLong();
/* perform as much work as needed here */
return Task.CompletedTask;
}
這將同步運行且僅同步運行。實際等待的是您回傳的 Task.CompletedTask 物件,僅此而已。并且由于 Task 已經完成,內部的 awaiter 也被標記為已完成。
這意味著盡管意圖可能是在一個 Task 中包裝多個方法,然后執行/等待它,但實際發生的是這些方法像任何其他呼叫一樣同步執行,然后回傳一個已完成的 Task。
如果您希望等待多個方法,可以通過創建一個新的 Task Object來完成。使用我們之前的示例:
public async Task DoSomeReallyLongWorkAsync()
{
/// Task.Run does not necessarily run on a separate thread
/// this is up to the scheduler (usually the .NET scheduler)
await Task.Run(LongSyncTasksWrapped);
}
public void LongSyncTasksWrapped()
{
SyncTaskThatIsReallyLong();
AnotherSyncTaskThatIsReallyLong();
/* perform as much work as needed here */
return;
}
在某些情況下,您可能需要冷任務(尚未啟動的任務),然后在需要時運行它們。使用前面的示例,這將通過以下方式完成:
public async Task DoSomeReallyLongWorkAsync()
{
var coldTask = new Task(LongSyncTasksWrapped);
/// Must call .Start() whenever you want the Task to
/// actually start. Await will not start the Task, its
/// just an asynchronous form of .Wait()
coldTask.Start();
/// coldTask was considered "hot" from .Start()
/// await is waiting a hot task.
await coldTask;
}
public void LongSyncTasksWrapped()
{
SyncTaskThatIsReallyLong();
AnotherSyncTaskThatIsReallyLong();
/* perform as much work as needed here */
return;
}
這回答了正在等待什么的問題,它是由類內部生成的任務的等待者。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/496639.html
上一篇:為什么將實體變數分配給區域變數?
下一篇:Python:如果佇列突然從另一個執行緒/行程中銷毀,執行緒是否會在掛起`Queue.get(block=True)`呼叫時解除阻塞?
