BackgroundWorker线程和定时器逻辑

我一直在尝试为我的计时器和后台工作者线程提供正确的逻辑。 虽然我阅读了所有内容,但我并不完全了解整个系统。 以下是相关代码的摘录:我的轮询按钮:

private void pollStart_Click(object sender, EventArgs e) { tst_bgw = new BackgroundWorker(); //mandatory. Otherwise will throw an exception when calling ReportProgress method tst_bgw.WorkerReportsProgress = true; //mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation tst_bgw.WorkerSupportsCancellation = true; tst_bgw.DoWork += tst_bgw_DoWork; tst_bgw.ProgressChanged += tst_bgw_ProgressChanged; tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted; tst_bgw.RunWorkerAsync(); } 

我认为到目前为止是正确的

我的后台工作线程:

 private void tst_bgw_DoWork(object source, DoWorkEventArgs e) { m_timer = new System.Timers.Timer(); m_timer.Interval = 1000; m_timer.Enabled = true; m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent); if (tst_bgw.CancellationPending) { e.Cancel = true; return; } } 

和已过去的层事件代码:

 private void OnTimedEvent(object source, ElapsedEventArgs e) { if (powerVal > 3250) { m_timer.Stop(); tst_bgw.CancelAsync(); } else { string pow; int progressVal = 100 - ((3250 - powerVal) / timerVal); uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it. pow = construct_command("power", powerVal); sData = Encoding.ASCII.GetBytes(pow); if (active_connection) try { m_sock.Send(sData); Array.Clear(sData, 0, sData.Length); tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK. m_sock.Send(time_out_command); tstDel(ref unit_Output); tst_bgw.ReportProgress(progressVal); } catch (SocketException se) { MessageBox.Show(se.Message); } tst_bgw.ReportProgress(powerVal, progressVal); powerVal = powerVal + pwrIncVal; } 

我只想知道其他一些事情; 我使用的是正确的计时器(不是我觉得它应该很重要,但有人建议这可能是我想要做的最好的计时器)而且我真的可以通过代表修改DoWork方法中的UI元素,如果是的话这样做有特殊的考虑因素。 对于长篇post感到抱歉,谢谢你的时间。

这段代码有很多问题。

1)您没有处置您的后台工作人员。 背景工人必须在使用后进行处理。 它们被设计用作winforms组件,通常会通过设计器添加到窗口中。 这将确保它与表单一起创建并在表单处理时丢弃。
2)你在dowork方法中所做的就是创建一个新计时器并运行它。 在后台工作者中没有必要这样做,因为无论如何它会很快发生。
3)每次再次运行后台工作程序时,您将重新创建计时器。 但你永远不会停止或处理旧计时器,你只是覆盖了成员。

我建议你完全摆脱BackgroundWorker,只需使用一个计时器。 在表单构造函数中创建计时器,并确保在窗体dispose方法中处理它。 (或者使用设计器将其添加到表单中)。 在pollstart_click方法中,只需启动计时器。 (如果你有一个轮询停止方法,你可以在那里停止计时器)

您不需要BackgroundWorker和Timer来实现目标。 根据您发布的内容,您似乎希望让用户单击一个按钮,该按钮启动在certian点退出的轮询过程。

您的轮询模型确实表明计时器可以正常工作。

如果您使用Timer,我会在InitializeComponent()调用之后用类似的方法初始化定时器

 private void InitializeTimer() { this.timer = new Timer(); int seconds = 1; this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds this.timer.Tick += new EventHandler(timer_Tick); // don't start timer until user clicks Start } 

button_click会很简单

 private void button_Click(object sender, EventArgs e) { this.timer.Start(); } 

然后在timer_Tick上你将需要进行轮询,你应该能够从那里更新你的UI,如果计时器在这样的UI线程上

 void timer_Tick(object sender, EventArgs e) { if( determineIfTimerShouldStop() ) { this.timer.Stop(); } else { // write a method to just get the power value from your socket int powerValue = getPowerValue(); // set progressbar, label, etc with value from method above } } 

但是,如果计时器线程与UI不在同一个线程上,则在尝试更新UI时会出现exception。 在这种情况下,您可以使用DataDink提到的Invoke并执行类似的操作

 void timer_Tick(object sender, EventArgs e) { if( determineIfTimerShouldStop() ) { this.timer.Stop(); } else { // write a method to just get the power value from your socket int powerValue = getPowerValue(); // set a label with Invoke mylabel.Invoke( new MethodInvoker( delegate { mylabel.Text = "some string"; } ) ); } } 

鉴于您发布的代码,您实际上不需要同时使用BackgroundWorker和Timer,但我有一些实例,我在调用计时器时使用BackgroundWorker来工作,这样我就可以定期更新定时器UI有一个手动按钮来刷新UI。 但我并没有像你那样更新我的用户界面。

如果您仍然需要同时执行这两项操作,那么大致就是如何使您的应用程序流动…

  • 创建一个InitailizeBackgroundWorker()方法和InitializeTimer,以便在Timer触发之前已经初始化它。
  • 然后设置Timer.Tick以调用BackgroundWorker.RunWorkerAsync()
  • 然后,您可以使用BackgroundWorker.ReportProgress()从RunWorkerAsync中执行所有UI更新。