WinForms异步方法在foreach中提前迭代的原因咨询
问题分析与解决方案
嗨,我来帮你搞明白这个问题~
你遇到的核心问题,根源在于**async void方法的特性**。虽然你在GetInfoAsync里用了await,但因为这个方法是async void类型,当你在foreach循环里调用它时,调用方(循环)根本没办法等待它执行完成。
具体原因
当GetInfoAsync执行到await Task.Run(...)这一行时,它会立即把控制权交还给调用它的foreach循环,自己进入等待状态。这时候循环就直接跳到下一次迭代,不会等这个方法里的异步操作完成——这就是为什么你觉得“foreach没等GetInfoAsync做完就跑下一轮”的原因。
async void其实是专门为事件处理程序设计的(比如你的按钮点击方法GetInfoToolStripMenuItem_Click用async void是合理的),但普通的异步工作方法,我们应该用async Task作为返回类型,这样调用者才能等待它完成。
修复步骤
1. 把GetInfoAsync改成async Task返回类型
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; }
2. 修改按钮点击事件,根据需求选择并行或串行处理
如果你想同时获取所有服务器的数据(效率更高,推荐),用Task.WhenAll来等待所有任务完成:
private async void GetInfoToolStripMenuItem_Click(object sender, EventArgs e) { var tasks = new List<Task>(); foreach (DataGridViewRow row in infoDataGridView.Rows) tasks.Add(GetInfoAsync(row)); // 等待所有异步任务都完成 await Task.WhenAll(tasks); }
如果你想一个接一个地获取数据(串行执行),直接在循环里await每个GetInfoAsync调用:
private async void GetInfoToolStripMenuItem_Click(object sender, EventArgs e) { foreach (DataGridViewRow row in infoDataGridView.Rows) // 等当前服务器的数据获取完成,再处理下一个 await GetInfoAsync(row); }
额外提醒
async void还有个坑:如果方法里抛出异常,很难被捕获到。换成async Task后,你可以通过try/catch包裹await来处理异常,让程序更健壮。
内容的提问来源于stack exchange,提问作者Kiko
相关产品推荐
相关产品推荐

