在 Backgroundworker DoWork 中更改计时器间隔会禁用计时器 [C#]

Changing Timer Interval in Backgroundworker DoWork Disables the Timer [C#]

我在更改 backgroundworker 的 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;
    }
}

}

您从未将 push 设置为 false。

因此,代码如下:

    while(running)
    {
       if(push == true)
       {
           activate();  
       }
    }

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

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

尝试使用 UI 线程的调度程序调用您的 activate 方法。 (假设获胜形式?

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;
}

我花了一段时间,但我发现了问题所在。我会 post 你一个工作代码,以防万一有人遇到同样的问题。

public 部分 class Form1 : 表格 { public int ticks = 0; public 布尔 运行 = false; public bool 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;
    }
}

我可以看到很久以前有人问过这个问题,但供参考:

一般来说,当涉及到计时器或线程时(记住计时器是 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;
     }