BackgroundWorker线程和定时器逻辑

时间:2009-08-06 21:31:15

标签: c# .net user-interface multithreading

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

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元素,如果是的话这样做有特殊的考虑因素。 对于长篇帖子感到抱歉,谢谢你的时间。

2 个答案:

答案 0 :(得分:1)

此代码有很多错误。

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

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

答案 1 :(得分:1)

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

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

如果使用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时会出现异常。在这种情况下,您可以使用DataDink提及的调用并执行类似此操作的

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.Tick 打电话给 BackgroundWorker.RunWorkerAsync()
  • 然后,您可以在RunWorkerAsync中执行所有UI更新 使用 BackgroundWorker.ReportProgress()。