如何在异步方法中向UI提供反馈?
我一直在开发一个Windows表单项目,我有10个任务要做,我想以async
方式执行此操作。 当用户单击按钮并且我调用async
方法执行此操作时,这些任务将会出现。 在我的代码中,我已经有了这些进程的参数列表。
我的问题是:
A)如何转换我的代码并行运行所有进程? (我想实现async / await)
B)如何向我的UI应用程序提供反馈?
下面的代码是我试过的:
我的按钮调用方法来启动进程
private void button1_Click(object sender, EventArgs e) { // almost 15 process foreach (var process in Processes) { // call a async method to process ProcessObject(process); } }
模拟我的进程获取参数的方法
private async void ProcessObject(ProcessViewModel process) { // this is my loop scope, which I need to run in parallel { // my code is here // increment the progress of this process process.Progress++; // feedback to UI (accessing the UI controls) UpdateRow(process); } }
我试过这个,但我不确定它是否是更新我的UI(网格)的正确方法。
private void UpdateRow(ProcessViewModel process) { dataGridView1.Rows[process.Index - 1].Cells[1].Value = process.Progress; dataGridView1.Refresh(); }
首先,对async void
方法说“ 不” (事件处理程序是例外),因为不会注意到它内部抛出的exception。 而是使用async Task
并等待它。
private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier { // almost 15 process foreach (var process in Processes) { // call a async method to process await ProcessObject(process); } } private async Task ProcessObject(ProcessViewModel process)// <--Note the return type { // my code is here with some loops await Task.Run(()=> { //Will be run in ThreadPool thread //Do whatever cpu bound work here }); //At this point code runs in UI thread process.Progress++; // feedback to UI UpdateRow(process); }
但是,这将一次只启动一个任务,一旦完成,它将在下一个启动。 如果你想立即启动所有这些,你可以启动它们并使用Task.WhenAll
等待它。
private async void button1_Click(object sender, EventArgs e)// <--Note the async modifier { var tasks = Processes.Select(ProcessObject).ToList(); await Task.WhenAll(tasks); }
Sriram 在他的回答中介绍了如何进行异步/等待,但我想向您展示另一种方法,以他的回答作为基础来更新UI线程的进度。
.NET 4.5添加了IProgress
接口和Progress
类。 内置的Progress类捕获同步上下文,就像async / await那样,然后当你去报告你的进度时,它使用该上下文来进行回调(在你的情况下是UI线程)。
private async void button1_Click(object sender, EventArgs e) { var progress = new Progress(UpdateRow); //This makes a callback to UpdateRow when progress is reported. var tasks = Processes.Select(process => ProcessObject(process, progress)).ToList(); await Task.WhenAll(tasks); } private async Task ProcessObject(ProcessViewModel process, IProgress progress) { // my code is here with some loops await Task.Run(()=> { //Will be run in ThreadPool thread //Do whatever cpu bound work here //Still in the thread pool thread process.Progress++; // feedback to UI, calls UpdateRow on the UI thread. progress.Report(process); }); }