在后台工作DoWork中改变定时器间隔禁用定时器[c#]

本文关键字:定时器 DoWork 工作 改变 后台 | 更新日期: 2023-09-27 18:04:11

我有在后台工作者的DoWork事件改变定时器间隔的问题。当通过点击按钮更改间隔时,计时器停止并且不会再次启动。

有谁知道如何解决这个问题吗?简单代码:

    public Form1()
    {
        InitializeComponent();
        timerTest.Tick += new EventHandler(timerTest_Tick);
        timerTest.Interval = 1000;
        timerTest.Start();
    }
    private void buttonTest_Click(object sender, EventArgs e)
    {
        push = true;
    }
    private void timerTest_Tick(object sender, EventArgs e)
    {
        ticks++;
        labelTest.Text = ticks.ToString();
        if(running == false)
        {
            running = true;
            backgroundWorkerTest.RunWorkerAsync();
        }
    }
    public void activate()
    {
        timerTest.Stop();
        timerTest.Interval = 4000;
        timerTest.Start();
    }
    private void DoWork(object sender, DoWorkEventArgs e)
    {
        while(running)
        {
           if(push == true)
           {
               activate();  
           }
        }
    }
    private void Completed(object sender, RunWorkerCompletedEventArgs e)
    {
        running = false;
    }
}

}

在后台工作DoWork中改变定时器间隔禁用定时器[c#]

您从未将push设置为false。

因此,下面的代码:
    while(running)
    {
       if(push == true)
       {
           activate();  
       }
    }

将在一个紧循环中连续调用activate()activate()先停止定时器,然后重新启动定时器,调用它的时间间隔将远远小于定时器间隔。因此,计时器将永远不会留下足够长的时间来触发。

无论如何,为什么不直接从buttonTest_Click()调用activate()呢?

我知道这是很久以前问的,但作为参考:

当涉及到定时器或线程(记住定时器是system.threading)与后台工作者(任务)相结合时,永远不要试图在不知道工作者正在做什么的情况下随机更改线程属性。

当分配DoWork处理程序来准备后台工作进程Progress和Complete处理程序时,这总是一个很好的做法。在每个周期中,报告进度或完成情况,这将使您有机会进行检查并在需要时修改其他线程属性。
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
 while (!worker.CancellationPending)
{
  // do task 1 as per the timer1 interval
  // do task 2 as per such and such .....
  // if I call ChangeInterval here I'll be fiddling with another thread when 
  //  this is still in progress 
  // that a full loop, Progress will be reported now
  }
 }
   private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e)
     {
       // Now as the Do work is not in progress
       // do something 
       // check if the user wanted to change the interval ?
       // if yes then
       ChangeInterval(6000);
       // here the progress reporting is done so it will go back to DoWork with the 
       // NewInterval Value in place and the timer enabled
     }
     private void ChangeInterval(int NewInterval)
     {
       timer1.Enabled =false;
       timer1.Interval = NewInterval;
       timer1.Enabled = true;
     }
 

尝试用UI线程的Dispatcher调用activate方法。(假设Win Forms?)

this.Invoke(new Action(activate));

原因是你的timer是一个UI控件,你在一个单独的线程上更新Interval。这会抛出一个跨线程异常

为什么看不到异常?BackgroundWorker中的DoWork方法抛出异常时,它将被传播到Completed方法。因此,您应该始终查看e.Error,看看是否发生了异常。

private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
    if(e.Error != null)
    {
        // Oh no something went wrong...
    }
    running = false;
}

我花了一段时间才找到问题所在。我会给你一个工作代码,以防有人会有同样的问题。

public partial class Form1: Form{Public int ticks = 0;公共bool running = false;

push = false;
    public Form1()
    {
        InitializeComponent();
        timerTest.Tick += new EventHandler(timerTest_Tick);
        timerTest.Interval = 1000;
        timerTest.Start();
    }
    private void buttonTest_Click(object sender, EventArgs e)
    {
        push = true;
    }
    private void timerTest_Tick(object sender, EventArgs e)
    {
        ticks++;
        labelTest.Text = ticks.ToString();
        if(running == false)
        {
            running = true;
            backgroundWorkerTest.RunWorkerAsync();
        }
    }
    public void activate()
    {
        ZmienIntervalNaAwaryjny = true;
    }
    public bool ZmienIntervalNaAwaryjny = false;
    private void DoWork(object sender, DoWorkEventArgs e)
    {
           if(push == true)
           {
               activate();
           }
    }
    private void Completed(object sender, RunWorkerCompletedEventArgs e)
    {
        if(ZmienIntervalNaAwaryjny == true)
        {
            timerTest.Stop();
            timerTest.Interval = 12000;
            timerTest.Start();
        }
        ZmienIntervalNaAwaryjny = false;
        running = false;
    }
}