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,最终将阻止用户交互。
就您的代码而言,您应该使用 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 线程上不断执行内容。不要那样做,事情会好得多。