[英]UI blocked with Async/Await
在这里的许多线程中都以某种方式提出了这个问题,但是我找不到我的情况的明确答案,我有一个简单的代码,一个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。 但是,它肯定会使它保持真正的忙碌状态,这可能会使其看起来好像被阻止。 因此,即使不是真的,也可以出于所有实际目的将其阻止。
您基本上是在对GUI线程执行拒绝服务攻击。 您有一个紧密循环的辅助线程,该线程仅在GUI线程上继续执行操作。 不这样做,情况会好得多。
正如其他人指出的那样,如果您尝试过于频繁地更新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);
}
}
无需人为延迟。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.