【问题标题】:async method in foreachforeach 中的异步方法
【发布时间】:2018-06-18 15:37:38
【问题描述】:

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

我有一个从中获取信息的服务器列表。因此,当我单击“从所有人中获取”按钮时,我会从每个人那里获取信息。

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 关键字,所以在数据完成之前执行不应该完成。

【问题讨论】:

  • 你没有awaitGetInfoAsync
  • 您没有在 click 事件处理程序中使用 await,它们都是一劳永逸的。
  • @Servy "主要问题是:为什么 foreach 在 GetInfoAsync 完成之前开始新的迭代?"
  • @Servy The main question is: why foreach starts new iteration before GetInfoAsync finished?
  • GetInfoAsync 是一种即发即弃的方法,async void,它不返回任务。但是,它会在第一次遇到await T 时立即返回,其中T 是在您尝试等待它时尚未完成的任务,这是您代码中的Task.Run 部分。因此,您的循环将调用GetInfoAsync,它将执行直到您开始另一个任务,然后返回到您的循环。如果您想在继续循环之前完成此异步方法,请将其更改为 async Task 并在循环中等待它。

标签: c# winforms asynchronous multitasking


【解决方案1】:

原因为什么你的循环会移动到下一个项目并在前一个项目完成之前再次调用 GetInfoAsync 是因为 GetInfoAsync 是一种即发即弃的方法。

它是一劳永逸的,因为它已被声明为 async void 并在内部启动一个新任务。

GetInfoAsync等待此子任务这一事实不会神奇地使GetInfoAsync 方法等待直到此子任务完成后再返回。

相反,会发生以下情况:

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

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

如果您希望循环调用 GetInfoAsyncwait,然后再转到下一项,您需要使用循环更改方法:

  1. 也需要async
  2. 需要awaitGetInfoAsync方法
  3. 因此GetInfoAsync 也必须返回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;
}

【讨论】:

    猜你喜欢
    • 2018-08-03
    • 1970-01-01
    • 2012-10-15
    • 2012-06-16
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 1970-01-01
    相关资源
    最近更新 更多