c# 在循环中等待任务

c# wait for a task inside a loop

我通过以下方式在 Windows 表单应用程序中使用 TPL:

foreach (var item in items)
{
    task = Task<object>.Factory.StartNew (() => doWork());
    task.Wait();
    //update the UI using the result
}

我正在等待任务完成,因为我需要处理列表中的每个项目,但正如您想象的那样,这导致我的 UI 线程锁定(UI 冻结).

我想知道如何以不锁定 UI 线程的方式实现它。

更新:我正在使用 .NET Framework 4.5

谢谢。

P.S DoWork() 是一个很长的 运行 操作。

创建事件处理程序 async,并在代表线程池操作的任务上调用 await。这将导致您的事件处理程序放弃对 UI 线程的控制,直到任务完成,然后恢复更新 UI 并开始下一个任务。

private async void MyButton_Click(object sender, EventArgs e)
{
    foreach (var item in items)
    {
        // assuming C# 5 closure semantics
        await Task.Run(() => doWork(item));

        // update the UI using the result
    }
}

上述方法假定您希望按顺序 运行 您的任务。如果你想并行启动它们并按​​完成顺序处理它们的结果,你可以使用:

private async void MyButton_Click(object sender, EventArgs e)
{
    var tasks = items.Select(item => Task.Run(() => doWork(item))).ToList();
    while (tasks.Any())
    {
        var task = await Task.WhenAny(tasks);
        tasks.Remove(task);
        var result = await task;

        // update the UI using the result
    }
}

您可以通过多种方式解决此问题。

一种方法是不要在线程池线程上执行列表中的每个项目,而是将整个 foreach 循环放入 Task 队列中,然后在它们完成时更新结果。

public async void SoneEventHandler(object sender, EventArgs e)
{
    var result = await Task.Run(() => items.Select(item => DoWork()).ToList());
    foreach (var item in items)
    {
        // Update UI
    }
}

如果您仍想处理线程池威胁中的每个项目,请在 Task 上使用 Task.Runawait,不要使用 Task.Wait() 阻塞:

public async void SoneEventHandler(object sender, EventArgs e)
{
    foreach (var item in items)
    {
        var result = await Task.Run(() => DoWork());
        // Update UI
    }
}