UI被Async/Await阻止

Aym*_*udi 0 .net c# events asynchronous async-await

这个问题在某种程度上被问到了很多线程,但我找不到一个明确的答案我的情况,我有一个简单的代码,a Button,a Textbox和a 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
        }

    }
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

首先,为什么会发生这种情况?其次,我应该做些什么来使UI保持正常工作而不会挂起,总是遵循这个逻辑.

Pet*_*iho 5

定义"阻止".您发布的代码不会阻止UI.但它肯定会让它真的很忙,这可能会让它看起来好像被阻挡了.就此而言,即使不是真的,也要阻止它用于所有实际目的.

您基本上是在GUI线程上执行拒绝服务攻击.你有一个紧密循环的辅助线程,只是继续在GUI线程上执行东西.不要这样做,事情会好得多.

  • +1.@AymenDaoudi UI具有响应能力,您只需慢慢与CPU生成的请求竞争.当您降低来自其他线程的请求速率(即"睡眠")时,您有机会响应您的(以及WM_PAINT)请求. (5认同)

Ste*_*ary 5

正如其他人所指出的,如果您过于频繁地尝试更新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);
  }
}
Run Code Online (Sandbox Code Playgroud)

不需要人工延迟.