C# BackgroundWorker的DoWork循环未完成提前退出问题
Ping检测程序循环随机提前终止问题
我开发了一款Ping检测程序,核心运行逻辑为:遍历共8个Ping对象逐个发送Ping请求,等待1000ms后重复执行上述流程,总循环次数由用户输入的运行秒数决定。
当设置程序运行时长为2-3秒时,循环运行完全符合预期,每个Ping对象的成功Ping计数准确无误;但将运行时长设置为10秒(即循环计数变量count=10)时,循环通常在执行到计数变量i=6左右就会意外终止,直接触发RunWorkerCompleted事件,实际循环执行次数完全随机。
我最初怀疑是线程相关故障,曾尝试移除wait方法中的Application.DoEvents()逻辑排查干扰,但问题仍存在:设置count=10进行两次测试,第一次仅执行1次循环就退出,第二次执行7次循环后退出。
相关实现代码
BackgroundWorker的DoWork事件处理方法
private async void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { try { // Create background worker BackgroundWorker worker = (BackgroundWorker)sender; // Write to log file await file.WriteAsync("Starting job...\n"); await file.WriteAsync("Requested amount of pings: " + count + "\n"); // Create date object for logs DateTime localDate = DateTime.Now; for (int i = 0; i < count; i++) { // Create ping objects System.Net.NetworkInformation.Ping pinger = new System.Net.NetworkInformation.Ping(); PingReply pingReply; foreach (Ping pingObj in pings) { try { pingReply = pinger.Send(pingObj.IpAddress); // Write log file await file.WriteLineAsync(localDate.TimeOfDay + " | Friendly Name " + pingObj.FriendlyName + " | Ping: " + pingReply.Address + " | Status " + pingReply.Status + " | Time: " + pingReply.RoundtripTime); if (pingReply.Status.ToString().Contains("Success")) { pingObj.SuccessfulPings += 1; } else // Unsuccessful ping has been sent { pingObj.FailedPings += 1; } } catch (Exception ex) { Debug.WriteLine(ex.ToString()); } } wait(1000); } } catch (Exception a) { Debug.WriteLine(a.ToString()); } }
按钮点击启动BackgroundWorker的事件处理方法
private void Ping_Btn_Click(object sender, EventArgs e) { int counter = 1; if (backgroundWorker1.IsBusy != true) { // For every ping object foreach (Ping pinger in pings) { // Set the appropriate UI element properties // Create initial control object // Finds controls in which their name matches the string Control? ctl = this.Controls.Find("greenBox" + counter, true).FirstOrDefault() as Control; if (ctl != null) { ctl.Visible = false; } ctl = this.Controls.Find("orangeBox" + counter, true).FirstOrDefault() as Control; if (ctl != null) { ctl.Visible = false; } ctl = this.Controls.Find("redBox" + counter, true).FirstOrDefault() as Control; if (ctl != null) { ctl.Visible = true; } ctl = this.Controls.Find("status_Lbl" + counter, true).FirstOrDefault() as Control; if (ctl != null) { ctl.Text = "Initiated..."; } counter++; } // Take input from text box count = Convert.ToInt32(pingSeconds_TxtBox.Text); // Start operation backgroundWorker1.RunWorkerAsync(); } }
自定义毫秒等待wait方法
public void wait(int milliseconds) { var timer1 = new System.Windows.Forms.Timer(); if (milliseconds == 0 || milliseconds < 0) return; timer1.Interval = milliseconds; timer1.Enabled = true; timer1.Start(); timer1.Tick += (s, e) => { timer1.Enabled = false; timer1.Stop(); }; while (timer1.Enabled) { Application.DoEvents(); } }
故障根因
BackgroundWorker的DoWork事件设计为在后台线程执行同步任务,给事件处理方法加async void修饰后,方法执行到第一个await断点时就会直接返回,BackgroundWorker会判定任务已执行完毕,立刻触发RunWorkerCompleted事件,这就是循环随机提前终止的核心原因。- 自定义
wait方法使用System.Windows.Forms.Timer搭配Application.DoEvents()实现阻塞等待是WinForm开发中的典型反模式,会打乱UI线程消息泵的处理顺序,引入不可预知的重入问题,进一步加剧了异步状态错乱的故障。
最终解决方案
移除BackgroundWorker组件,改用async/await异步模式实现相关逻辑,该方案由Charlieface提供。
内容的提问来源于stack exchange,提问作者Thomton
相关产品推荐
相关产品推荐

