如何在不阻止UI线程的情况下等待所有任务完成?

在下面的代码中,我在处理任务之前禁用按钮,并希望在所有任务完成后启用它。

List tasks = new List(); buttonUpdateImage.Enabled = false; // disable button foreach (OLVListItem item in cellsListView.CheckedItems) { Cell c = (Cell)(item.RowObject); var task = Task.Factory.StartNew(() => { Process p = new Process(); ... p.Start(); p.WaitForExit(); }); task.ContinueWith(t => c.Status = 0); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); // enable button here 

WaitAll正在阻止UI线程。 我怎么能等到所有任务完成然后启用按钮?

首先,我将安装Microsoft.Bcl.Async ,它将支持在.NET 4.0中使用async-await

现在,使用这个问题的答案,您可以异步注册进程退出,而无需使用Task.Factory.StartNew

 public static class ProcessExtensions { public static Task RunProcessAsync(this Process process, string fileName) { if (process == null) throw new ArgumentNullException(nameof(process)); var tcs = new TaskCompletionSource(); process.StartInfo = new ProcessStartInfo { FileName = fileName }; process.EnableRaisingEvents = true process.Exited += (sender, args) => { tcs.SetResult(true); process.Dispose(); }; process.Start(); return tcs.Task; } } 

现在,您可以这样做:

 buttonUpdateImage.Enabled = false; // disable button var tasks = cellsListView.CheckedItems.Cast() .Select(async item => { Cell cell = (Cell)item.RowObject; var process = new Process(); await process.RunProcessAsync("path"); cell.Status = 0; }); await Task.WhenAll(tasks); buttonUpdateImage.Enabled = true; 
 List tasks = new List(); buttonUpdateImage.Enabled = false; // disable button foreach (OLVListItem item in cellsListView.CheckedItems) { Cell c = (Cell)(item.RowObject); var task = Task.Factory.StartNew(() => { Process p = new Process(); ... p.Start(); p.WaitForExit(); }).ContinueWith(t => c.Status = 0); // this modification is very important, Please note it, this will fix a problem tasks.Add(task); } // your code has been modified to just make the task object // is the continued task instead of the task is the original task // Task.WaitAll and add this instead of it. Task.Factory.ContinueWhenAll(tasks.ToArray(), ac => buttonUpdateImage.Enabled = true;); 

其他方式 …..

 // Note this object. Barrier b = new Barrier(cellsListView.CheckedItems.Count, () => { // enable the button here buttonUpdateImage.Enabled = true; }); buttonUpdateImage.Enabled = false; // disable button foreach (OLVListItem item in cellsListView.CheckedItems) { Cell c = (Cell)(item.RowObject); var task = Task.Factory.StartNew(() => { Process p = new Process(); ... p.Start(); p.WaitForExit(); }).ContinueWith(t => { c.Status = 0; b.SignalAndWait(); }); } 

第一种方式比第二种方式更好,我建议你使用第一种方式。

您可以创建一个新任务来运行WaitAll方法,就像

 Task.Factory.StartNew(() => { Task.WaitAll(tasks.ToArray()); Action act = () => { this.button1.Enabled = true; }; this.button1.Invoke(act); });