C# 使用异步等待仍然冻结GUI
我希望在单独的线程中处理长时间运行的操作,并使用异步/等待模式尽快将控制返回GUI线程,如下所示:C# 使用异步等待仍然冻结GUI,c#,user-interface,asynchronous,async-await,C#,User Interface,Asynchronous,Async Await,我希望在单独的线程中处理长时间运行的操作,并使用异步/等待模式尽快将控制返回GUI线程,如下所示: private async void Button_Click(object sender, RoutedEventArgs e) { await Test(); txtResult.Text = "Done!"; } private Task Test() { Thread.Sleep(3000); return Task.FromResult(0); } 问
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Test();
txtResult.Text = "Done!";
}
private Task Test()
{
Thread.Sleep(3000);
return Task.FromResult(0);
}
问题是,它会将GUI冻结3秒钟(在3秒钟后显示“完成”之前,它将变得无响应)。我做错了什么
编辑:
我试图替换以下逻辑:
private void Button_Click(object sender, RoutedEventArgs e)
{
var thread = new Thread(() => Test(Callback));
thread.Start();
}
private void Callback()
{
Dispatcher.Invoke(() =>
txtResult.Text = "Done!");
}
private void Test(Action callback)
{
Thread.Sleep(3000); //long running operation, not necessarily pause
callback();
}
在实际的项目中,我有不同于睡眠的长时间运行逻辑,它仍然冻结GUI,所以用任务代替它。延迟并不能解决任何问题。此外,我不明白你为什么还要使用另一个睡眠命令?异步/等待设计如何要求这一点?您使用的是阻止线程的
线程。Sleep
。
改为使用Task.Delay
,它在内部使用计时器并异步将控制权返回给调用方:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await TestAsync();
txtResult.Text = "Done!";
}
private async Task<int> TestAsync()
{
await Task.Delay(3000);
return 0;
}
您可以使用
Task.Run
或Task.Factory.StartNew
在另一个线程上执行Test()
或一些长时间运行和/或阻塞操作:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() => Test());
txtResult.Text = "Done!";
}
异步的可能重复不会导致代码在单独的线程上运行。它允许在创建要在另一个线程上运行的任务或执行要等待的IO绑定任务的代码上使用当前线程时不被阻止。不过,您的方法不做这两件事。感谢您的回复,在实际项目中,我有长时间运行的逻辑,而不是睡眠,请参阅编辑的问题。好的,所以等待任务。运行(测试)异步运行,而等待任务同步运行?wait Task()与直接调用Task()相比有什么意义?@EugeneKr
wait
只是一个用于向编译器标记简单提示的关键字。异步不是由await
完成的,而是由正在等待的底层方法完成的。这意味着,例如,如果您正在使用HttpClient
执行异步IO,它公开了自然异步方法,那么当您wait
它的一个方法时,控件将返回到调用方法。当您使用Task.Run
时,您是在线程池线程上显式运行委托,这与使用自然异步API不同,后者不需要额外的线程。我开始理解这一点。。。我想是吧。仍然没有看到它在哪里有用。尽管如果我面前有一段使用await Test()的示例代码会容易得多,如果可能的话,与我提供的代码类似。希望你还有点耐心:)我几乎看不出等待任务的例子。延迟是有用的。@EugeneKr我不太清楚你的意思<代码>等待测试(),按照您的示例,不异步执行任何操作。它有一个阻塞部分,使用Thread.Sleep
模拟,并使用实用工具Task。FromResult
同样不异步,它只使用TaskCompletionSource
创建一个已完成的Task
对象。等待它只会阻止调用线程。对不起,为什么它应该是等待任务。运行(()=>Test());而不仅仅是等待Test()?Test()返回一个同步任务,仅仅等待它不会在单独的线程上启动任务。对于这一点的解释比我的解释要好得多:“async和await关键字不会导致创建额外的线程。”以及“您可以使用Task.Run将CPU绑定的工作移动到后台线程”。Task.Run是任务并行库的一部分。在这种情况下,它很好地发挥了等待的作用。在c#中有很多方法可以在后台做一些事情。为了更好地理解,我建议阅读此快速演练
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() => Test());
txtResult.Text = "Done!";
}