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

本文关键字:等待 情况下 任务 线程 UI | 更新日期: 2023-09-27 18:12:11

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

List<Task> tasks = new List<Task>();
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线程。如何等待所有任务完成,然后启用该按钮?

如何在不阻塞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<bool>();
        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<OLVListItem>()
                                      .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<Task> tasks = new List<Task>();
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);
});