我正在编写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 关键字,因此在数据完成之前执行不应该完成。
答案 0 :(得分:4)
原因为什么你的循环将转移到下一个项目并再次调用GetInfoAsync
,之前的完成是因为GetInfoAsync
是火 - 和 - 忘记方法。
它被点燃了,因为它已被声明为async void
并在内部开始一项新任务。
GetInfoAsync
等待这个子任务的事实并没有神奇地使GetInfoAsync
方法等待,直到此子任务完成之前它会回来。
相反,这是发生的事情:
GetInfoAsync
。GetInfoAsync
会一直执行,直到它使用Task.Run
生成子任务。await T
的魔力在此子任务之后排队继续任务第一行的子任务完成后,将安排继续任务执行。
您希望循环调用GetInfoAsync
和等待,然后再转到下一个需要使用循环更改方法的项目:
async
await
GetInfoAsync
方法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;
}