foreach 中的异步方法

async method in foreach

我正在编写 Windows 表单应用程序,但在从 FTP 服务器加载数据时遇到阻塞 UI 的问题。

我有一个服务器列表,我从中获取信息。因此,当我单击按钮 'Get from all' 时,我会从每个按钮中获取信息。

private void GetInfoToolStripMenuItem_Click(object sender, EventArgs e)
{
    foreach (DataGridViewRow row in infoDataGridView.Rows)
        this.GetInfoAsync(row);
}

我在从服务器获取信息时 运行 新任务,以防止 UI 阻塞。 GetInfo 方法是异步的:

private async void GetInfoAsync(DataGridViewRow row)
{
    Server server = (Server)row.Cells[0].Value;
    row.Cells[1].Value = Resources.GettingIcon;

    await Task.Run(() =>
    {
        server.Data = dataGetter.GetData(server);
    });

    row.Cells[1].Value = Resources.FinishedIcon;
}

主要问题是:为什么 foreach 在 GetInfoAsync 完成之前开始新的迭代?我在此方法中使用了 await 关键字,因此执行不应在数据完成之前完成。

原因为什么你的循环将移动到下一个项目并在前一个完成之前再次调用 GetInfoAsync 是因为 GetInfoAsync一劳永逸的方法。

它是即发即弃的,因为它已被声明为 async void 并在内部启动了一个新任务。

事实上 GetInfoAsync 等待 这个子任务并没有神奇地使 GetInfoAsync 方法 等待直到这个子任务在它之前完成 returns.

相反,会发生以下情况:

  1. 您的循环将为第一行调用 GetInfoAsync
  2. GetInfoAsync 将一直执行到使用 Task.Run.
  3. 生成子任务为止
  4. 然后它将使用 await T 的魔力在这个子任务之后排队继续任务
  5. 然后它会return回到循环

第一行的子任务完成后,将安排执行后续任务。

如果您希望循环调用 GetInfoAsync等待 ,然后再移动到下一个项目,您需要使用循环更改方法:

  1. 也需要async
  2. 它需要await GetInfoAsync 方法
  3. 因此 GetInfoAsync 也必须 return 一个 Task

这是您的最终代码:

private async void GetInfoToolStripMenuItem_Click(object sender, EventArgs e)
{
    foreach (DataGridViewRow row in infoDataGridView.Rows)
        await this.GetInfoAsync(row);
}

private async Task GetInfoAsync(DataGridViewRow row)
{
    Server server = (Server)row.Cells[0].Value;
    row.Cells[1].Value = Resources.GettingIcon;

    await Task.Run(() =>
    {
        server.Data = dataGetter.GetData(server);
    });

    row.Cells[1].Value = Resources.FinishedIcon;
}