如何在不阻止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); });