WinForm中MySqlConnection异步方法阻塞UI,表现如同步方法
WinForm异步方法导致进度条延迟显示问题
这是一个WinForm应用,ProgressBarz是我自定义的简单进度条,但它只会在ImportaOrdiniAsync方法完全执行完成后才会显示,整个方法的表现和同步方法毫无区别。
相关代码
private async void ultraButtonImporta_Click(object sender, EventArgs e) { if (ultraComboStagione.Value != null) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Title = "Seleziona file da Importare"; openFileDialog.DefaultExt = "xlsx"; openFileDialog.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm"; if (openFileDialog.ShowDialog() == DialogResult.OK) { var task = ImportaOrdiniAsync(openFileDialog.FileName, ultraComboStagione.Value.ToString()); ProgressBarz caricamento = new ProgressBarz (); caricamento.Show(); Application.OpenForms["OrdiniLimiti"].Enabled = false; await task; caricamento.Close(); Application.OpenForms["OrdiniLimiti"].Enabled = true; } else { MessageBox.Show("Selezionare un file Excel!"); } } else { MessageBox.Show("Selezionare una Stagione!"); } } private async Task ImportaOrdiniAsync(string fileName, string stagione) { Workbook wb = Workbook.Load(fileName); Worksheet ws = wb.Worksheets[0]; for (int i = 1; i < ws.Rows.Count(); i++) { //Validation Here... int idOrdine = await IsOrdinePresenteAsync(codiceCliente, stagione); if (idOrdine != -1) { //Other stuff } else { //Other stuff } } //Other stuff } private async Task<int> IsOrdinePresenteAsync(string codiceCliente, string stagione) { using (MySqlConnection conn = new MySqlConnection( AdoFunction.brMySqlConnection.brMyStringConnection)) { MySqlCommand cmd = new MySqlCommand("SELECT id from table " + "WHERE field1 = @codiceCliente AND field2 = @stagione", conn); cmd.Parameters.AddWithValue("@codiceCliente", codiceCliente); cmd.Parameters.AddWithValue("@stagione", stagione); await conn.OpenAsync(); object result = await cmd.ExecuteScalarAsync(); if (result != null) { return (int)result; } return -1; } }
问题出在IsOrdinePresenteAsync方法中的OpenAsync()或ExecuteScalarAsync()调用上。但如果我在IsOrdinePresenteAsync里替换成await Task.Delay,进度条就能正常显示。
问题原因与解决方案
核心原因
代码里的Workbook.Load(fileName)是同步阻塞操作,如果Excel文件体积较大,这一步会直接卡住UI线程,导致进度条还没来得及渲染,就被后续操作拖到方法结束才显示。另外ws.Rows.Count()如果是同步计算行数,也会进一步阻塞UI。
虽然IsOrdinePresenteAsync用了异步数据库方法,但UI线程已经被前面的同步Excel加载操作占住,根本没机会更新进度条。而Task.Delay是真正释放UI线程的异步操作,所以进度条能正常显示。
修复步骤
- 异步加载Excel文件:把同步的Excel加载操作放到后台线程,避免阻塞UI:
Workbook wb = await Task.Run(() => Workbook.Load(fileName));
- 异步获取行数:如果
ws.Rows.Count()是同步计算逻辑,同样用Task.Run包裹:
int rowCount = await Task.Run(() => ws.Rows.Count()); for (int i = 1; i < rowCount; i++) { // 后续逻辑不变 }
- 异步化所有同步阻塞操作:检查
//Validation Here...和//Other stuff里的代码,如果有同步耗时操作,都用Task.Run包裹后await,保证UI线程能及时响应进度条的渲染和更新。
这样修改后,UI线程不会被同步操作阻塞,进度条就能在ImportaOrdiniAsync执行过程中正常显示了。
内容的提问来源于stack exchange,提问作者Simone D.
相关产品推荐
相关产品推荐

