C# 如何使用异步连续更新GUI
刚刚创建了一个WPF项目.NET4.6 把这个代码放进去C# 如何使用异步连续更新GUI,c#,wpf,asynchronous,async-await,C#,Wpf,Asynchronous,Async Await,刚刚创建了一个WPF项目.NET4.6 把这个代码放进去 lbl1是GUI上的标签 但是标签从未更新,或者while循环仅继续1次 private void Button_Click_1(object sender, RoutedEventArgs e) { var t = Task.Run( async () => { await AsyncLoop(); }); }
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;
}
private void按钮\u单击\u 1(对象发送者,路由目标)
{
var t=Task.Run(
异步()=>
{
等待异步循环();
});
}
异步任务AsyncLoop()
{
while(true)
{
字符串结果=等待LoadNextItem();
lbl1.内容=结果;
}
}
私有静态int ir11=0;
异步任务LoadNextItem()
{
ir11++;
返回“aa”+ir11;
}
请使用user Application.Current.Dispatcher.Invoke访问ui线程
async Task AsyncLoop()
{
while (true)
{
string result = await LoadNextItem();
Application.Current.Dispatcher.Invoke(new Action(() => { lbl1.Content = result; }));
}
}
C#编译器会给你一个警告,告诉你问题出在哪里
具体而言,此方法不是异步的:
async Task<string> LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
或者,如果您没有异步操作,而是有一些紧密的CPU绑定循环,则可以通过任务将这些循环推送到后台线程。运行:
string LoadNextItem()
{
ir11++;
return "aa " + ir11;
}
while (true)
{
string result = await Task.Run(() => LoadNextItem());
lbl1.Content = result;
}
通过调用Task.Run,您断开了与GUI线程(或WPF调度程序)的关联(SynchronizationContext),并丢失了大部分async/await“goodness”
为什么不使用一个异步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;
}
private async void按钮\u单击\u 1(对象发送方,RoutedEventArgs e)
{
while(true)
{
字符串结果=等待LoadNextItem();
lbl1.内容=结果;
}
}
私有静态int ir11=0;
任务LoadNextItem()
{
等待任务。延迟(1000);//实际异步工作的占位符
ir11++;
返回“aa”+ir11;
}
或者,如果您确实想为“正在进行的”操作分离状态机,请尝试传递一个IProgress
(在这种情况下,默认的impl.Progress
或具体的Progress
应该可以很好地工作)。看
他的例子与你在问题中所说的非常接近。我把它复制到这里是为了保持独立
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);
}
}
public async void StartProcessingButton\u单击(对象发送方,事件参数e)
{
//进度构造函数捕获我们的UI上下文,
//因此lambda将在UI线程上运行。
变量进度=新进度(百分比=>
{
textBox1.Text=百分比+“%”;
});
//DoProcessing在线程池上运行。
等待任务。运行(()=>DoProcessing(progress));
textBox1.Text=“完成!”;
}
公共无效数据处理(IProgress progress)
{
对于(int i=0;i!=100;++i)
{
Thread.Sleep(100);//CPU绑定的工作
如果(进度!=null)
进度报告(一);
}
}
Edit:我必须承认,虽然Progress
是一个很好的抽象,但在本例中,它将落在Dispatcher.调用@Pamparanpa建议的调用上。的可能重复;具体看,我明白了。所以它实际上抛出了一个无声的错误,因此停止了。有没有办法快速键入或复制粘贴?Application.Current.Dispatcher.Invoke(新操作(()=>{是的,您可以在wpf中直接从另一个线程访问ui线程。这是等到ui更新后才进行的吗?还是仅仅是调度程序调用并立即返回?这是一个非阻塞操作。它可以工作,但错过了对async/await的固有支持,即在等待之后“返回”ui线程。在上述函数中等待的唯一优点是值得注意的是,在LoadNextItem上“等待”时,不会占用线程池线程。这是相当可观的,但在这种情况下,不会“完全”使用它。
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);
}
}