[英]Push async method's execution to thread pool thread
考慮這個Windows窗體代碼(可以編寫類似的WPF模擬代碼):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void TraceThreadInfo([CallerMemberName]string callerName = null)
{
Trace.WriteLine($"{callerName} is running on UI thread: {!this.InvokeRequired}");
}
private void DoCpuBoundWork([CallerMemberName]string callerName = null)
{
TraceThreadInfo(callerName);
for (var i = 0; i < 1000000000; i++)
{
// do some work here
}
}
private async Task Foo()
{
DoCpuBoundWork();
await Bar();
}
private async Task Bar()
{
DoCpuBoundWork();
await Boo();
}
private async Task Boo()
{
DoCpuBoundWork();
// e.g., saving changes to database
await Task.Delay(1000);
}
private async void button1_Click(object sender, EventArgs e)
{
TraceThreadInfo();
await Foo();
Trace.WriteLine("Complete.");
TraceThreadInfo();
}
}
這是Foo
/ Bar
/ Boo
方法的鏈,我想異步執行,而不阻塞UI線程。 這些方法類似,從某種意義上講,所有這些方法都會產生一些CPU限制的工作並最終調用“真正的”異步操作(例如,執行一些繁重的計算,將保存結果發送到數據庫)。
上面代碼的輸出是這樣的:
button1_Click正在UI線程上運行:True
Foo在UI線程上運行:True
Bar在UI線程上運行:True
Boo在UI線程上運行:True
完成。
button1_Click正在UI線程上運行:True
所以,所有這些東西都是同步執行的。
我知道通過內置的等待來捕獲當前的上下文。 所以,我想,這樣調用ConfigureAwait(false)
就足夠了:
private async Task Foo()
{
await Task.Delay(0).ConfigureAwait(false);
DoCpuBoundWork();
await Bar();
}
但實際上,這並沒有改變任何事情。
我想知道,這怎么可以“推”到線程池線程,假設,在button1_Click
方法結束時我需要返回到UI線程?
編輯 。
當參數為0
時, Task.Delay(0)
實際上優化了調用(感謝@usr為注釋)。 這個:
private async Task Foo()
{
await Task.Delay(1).ConfigureAwait(false);
DoCpuBoundWork();
await Bar();
}
將按預期工作(一切都在線程池上執行,除了button1_Click
的代碼)。 但這更糟糕:捕獲上下文或不捕獲取決於等待實施。
await Task.Delay(0).ConfigureAwait(false);
是一個糟糕的mans Task.Yield()
嘗試(這是行不通的,因為我猜如果參數為零,Delay會自行優化)。
我不建議在這里收益。
我認為在你的點擊處理程序中你應該推送到線程池:
await Task.Run(async () => await Foo());
這非常簡單,如果您調用的方法不依賴於UI的同步上下文,則始終有效。 這在架構上很好,因為您調用的方法不需要或應該知道它們被調用的代碼。
Task.Factory.StartNew()可用於在單獨的線程中輕松運行代碼。 您可以選擇通過調用Wait()使當前線程等待第二個線程。
以下是一個快速示例程序,演示了相關部分:
class Program
{
static void Main(string[] args)
{
Task t = Task.Factory.StartNew(() =>
{
//New thread starts here.
LongRunningThread();
});
Console.WriteLine("Thread started");
t.Wait(); //Wait for thread to complete (optional)
Console.WriteLine("Thread complete");
Console.ReadKey();
}
static void LongRunningThread()
{
Console.WriteLine("Doing work");
//do work here
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.