刚刚创建了一个WPF项目.net 4.6
已将此代码放入
lbl1
是GUI上的标签
但标签永远不会更新,或者while循环只会继续1次
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var t = Task.Run(
async () =>
{
await AsyncLoop();
});
}
async Task AsyncLoop()
{
while (true)
{
string result = await LoadNextItem();
lbl1.Content = result;
}
}
private static int ir11 = 0;
async Task<string> LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
答案 0 :(得分:2)
C#编译器会向您发出警告,告诉您问题所在。
具体来说,这种方法不是异步的:
async Task<string> LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
编译器消息将通知您此async
方法没有await
语句,因此将同步运行 。您应该只在有意义的地方使用async
,通常用于基于I / O的操作,例如:
async Task<string> LoadNextItemAsync()
{
await Task.Delay(100); // Placeholder for actual asynchronous work.
ir11++;
return "aa " + ir11;
}
或者,如果你没有进行异步操作,而是你有一些紧密的CPU绑定循环,那么你可以通过Task.Run
将它们推送到后台线程:
string LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
while (true)
{
string result = await Task.Run(() => LoadNextItem());
lbl1.Content = result;
}
答案 1 :(得分:2)
通过调用Task.Run,您破坏了与GUI线程(或WPF Dispatcher)的关联(SynchronizationContext),并丢失了大部分async / await&#39; goodness&#39;。
为什么不使用async void事件处理程序并返回每个步骤的SynchronizationContext(GUI线程/调度程序)?
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
while (true)
{
string result = await LoadNextItem();
lbl1.Content = result;
}
}
private static int ir11 = 0;
Task<string> LoadNextItem()
{
await Task.Delay(1000); // placeholder for actual async work
ir11++;
return "aa " + ir11;
}
或者,如果您真的想要将状态机分开,以便正常运行&#39;操作,尝试传递IProgress<T>
(默认impl。Progress<T>
或具体Progress<string>
在这种情况下应该很好用)。见this article by @Stephen Cleary
他的例子与你在问题中所说的非常接近。我已经将它复制到这里以获得SO独立性。
public async void StartProcessingButton_Click(object sender, EventArgs e)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var progress = new Progress<int>(percent =>
{
textBox1.Text = percent + "%";
});
// DoProcessing is run on the thread pool.
await Task.Run(() => DoProcessing(progress));
textBox1.Text = "Done!";
}
public void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i != 100; ++i)
{
Thread.Sleep(100); // CPU-bound work
if (progress != null)
progress.Report(i);
}
}
编辑:我必须承认,虽然Progress<T>
是一个很好的抽象,但在这种情况下,它只会落到Dispatcher.Invoke上,如@Pamparanpa建议的那样。
答案 2 :(得分:1)
请用户Application.Current.Dispatcher.Invoke访问ui线程
async Task AsyncLoop()
{
while (true)
{
string result = await LoadNextItem();
Application.Current.Dispatcher.Invoke(new Action(() => { lbl1.Content = result; }));
}
}