使用异步等待仍然冻结 GUI

本文关键字:冻结 GUI 等待 异步 | 更新日期: 2023-09-27 18:36:57

我想在单独的线程中处理长时间运行的操作,并使用异步/等待模式尽快将控制权返回给 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);
}
问题是,它

无论如何都会冻结 GUI 3 秒(它变得无响应,直到 3 秒后显示 Done!我做错了什么?

编辑:我正在尝试替换以下逻辑:

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();
}

在实际项目中,我有与 Sleep 不同的长时间运行的逻辑,它仍然冻结 GUI,因此用 Task.Delay 替换它并不能解决任何问题。此外,我不明白为什么要为睡眠使用另一个命令?异步/等待设计如何要求这样做?

使用异步等待仍然冻结 GUI

您可以使用

Task.RunTask.Factory.StartNew在另一个线程上执行Test()或一些长时间运行和/或阻塞操作:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(() => Test());
    txtResult.Text = "Done!";
}

您正在使用阻塞线程的Thread.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;
}

编辑

当您发布新代码时,我建议您隔离需要在日志运行过程之后运行的回调,并保存自己的Dispatcher.Invoke并使用async-await替换它,这将使您进入正确的同步上下文:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    await Task.Run(Test);
    CallBack();
}
private void Callback()
{
    txtResult.Text = "Done!"
}
private void Test()
{
    Thread.Sleep(3000); //long running operation, not necessarily pause
}