UI blocked with Async/Await

本文关键字:Await Async with blocked UI | 更新日期: 2023-09-27 18:27:25

这个问题不知何故在这里的许多线程中被问到,但我找不到我的情况的明确答案,我有一个简单的代码,一个Button,一个Textbox和一个ButtonClickEventHandler,后者尽管Async,但保持UI阻塞,代码:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            Task t = new Task(() =>
            {
                for (int i = 0; i < 10000; i++)
                {
                    Dispatcher.Invoke((()=>
                    {
                        TextBox.Text += i.ToString();
                    }));
                }
            });
            t.Start();
            await t;
            //Something else
        }
    }

Xaml :

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Margin="180,10,185,277" x:Name="Button" Click="ButtonBase_OnClick">
           Ok
        </Button>
        <TextBox Margin="29,83,40,33" x:Name="TextBox"></TextBox>
    </Grid>
</Window>

首先,为什么会这样?其次,我应该怎么做才能使 UI 保持正常工作而不会挂起,始终遵循此逻辑。

UI blocked with Async/Await

正如其他人所指出的,如果您尝试过于频繁地更新 UI,最终将阻止用户交互。

就您的代码而言,您应该使用 Task.Run 而不是任务构造函数,IProgress<T> 而不是 Dispatcher.Invoke 。我有一个为您处理限制的IProgress<T>实现,可以这样使用:

private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
  using (var progress = ObservableProgress<int>.CreateForUi(value => UpdateUi(value)))
  {
    await Task.Run(() => BackgroundOperation(progress));
  }
  ...
}
private static void UpdateUi(int progressUpdate)
{
  TextBox.Text += progressUpdate.ToString();
}
private static void BackgroundOperation(IProgress<int> progress)
{
  for (int i = 0; i < 10000; i++)
  {
    if (progress != null)
      progress.Update(i);
  }
}

无需人为延误。

定义"阻止"。您发布的代码不会阻止 UI。但它肯定会让它非常忙碌,这很可能会产生它看起来像被阻止的效果。就此而言,出于所有实际目的阻止它,即使它不是真的。

您基本上是在 GUI 线程上执行拒绝服务攻击。你有一个紧密循环中的辅助线程,它只是在 GUI 线程上不断执行内容。不要那样做,事情会好得多。