【问题标题】:Timer doesn't want to start again after it was disabled定时器在被禁用后不想重新启动
【发布时间】:2014-02-04 11:57:02
【问题描述】:

我正在编写一个简单的 C# 程序,它尝试使用 System.Forms.Timer 每 x 秒执行一次操作

tick 事件调用一个方法来启动一个新线程并禁用计时器,然后当线程完成它的工作时,它会再次启用计时器,但问题是,现在它在启用后不会滴答作响.

static System.Windows.Forms.Timer testtimer = new System.Windows.Forms.Timer();

    static void Main()
    {
        testtimer.Tick += testtimertick;
        testtimer.Interval = 5000;
        testtimer.Enabled = true;
        testtimer.Start();

        while (true)
        {
            Application.DoEvents();  //Prevents application from exiting
        }

    }

 private static void testtimertick(object sender, System.EventArgs e)
    { 

            testtimer.Enabled = false;
            Thread t = new Thread(dostuff);
            t.Start();
    }


private static void dostuff()
    {

       //Executes some code
       testtimer.Enabled = true; //Re enables the timer but it doesn't work
       testtimer.Start();
     }

【问题讨论】:

  • 只是为了在这里确认您的意图;您希望计时器触发,然后在 dostuff() 方法完成之前不触发?
  • 是的,确切地说,当 dostuff 没有完成时,计时器不应该启动。
  • 您是否尝试过timer.Stop(),然后在准备就绪时尝试timer.Start()?我也不太确定 Timer 是否是线程安全类,所以请检查this post out
  • testTimer.Enabled = true;相当于 testTimer.start();见:stackoverflow.com/questions/1012948/…

标签: c# multithreading timer


【解决方案1】:

As @grzenio said,您的问题似乎与您对在不同线程上创建的 Windows 窗体控件进行跨线程调用有关。

如果您使用.NET 4.5 (C# 5.0),我建议您查看async/await keywords,可以在Stephen Cleary's Blog 找到一个很好的介绍

如何使用 async 和 await 与旧版“DoStuff”的示例:

    private async void _Timer_Tick(object sender, EventArgs e)
    {
        _Timer.Enabled = false;

        await Task.Run((() => DoStuff()));

        _Timer.Enabled = true;
    }

注意事项:

  1. 已将异步添加到 Timer_Tick 事件的签名中。
  2. await 关键字与 Task.Run 一起用于异步运行 DoStuff。

使用这些关键字时,DoStuff 将异步运行,一旦 DoStuff 返回,它将使用最初调用 Tick 的线程的上下文在 await 之后继续在线。

【讨论】:

    【解决方案2】:

    不要在没有 GUI 的情况下使用 GUI 计时器。不要用DoEvents 旋转,因为你正在用它烧掉 100% 的 CPU 内核。使用System.Threading.Timer。它会起作用的。

    【讨论】:

      【解决方案3】:

      Windows 窗体控件不是线程安全的,您应该确保在 UI 线程上使用它们,参见例如C# Windows Forms Application - Updating GUI from another thread AND class?

      【讨论】:

        【解决方案4】:

        您可以使用System.Threading.Timer 做您想做的事情,使用Change Method 设置时间和周期,完成工作后重新启动即可。

        class Program
        {
            static System.Threading.Timer testtimer;
            static void Main(string[] args)
            {
                testtimer = new System.Threading.Timer(testtimertick);
                testtimer.Change(5000,0);
        
                Console.ReadLine();
            }
        
            private static void testtimertick(object sender)
            {
        
                Thread t = new Thread(dostuff);
                t.Start();
            }
        
        
            private static void dostuff()
            {
        
                //Executes some code
                Thread.Sleep(2000);
                Console.WriteLine("Tick");
                testtimer.Change(5000, 0);
            }
        }
        

        【讨论】:

        • 我想你想把那个 0 改成 Timeout.Infinite。那是testtimer.Change(5000, Timeout.Infinite);msdn.microsoft.com/en-us/library/2x96zfy7(v=vs.110).aspx
        • @JimMischel 实际上根据文档,他们都做同样的事情。 如果 period 为零 (0) 或 Infinite,且dueTime 不是 Infinite,则调用一次回调方法;定时器的周期性行为被禁用,但可以通过调用 Change 并从 MSDN 为 period 指定一个正值来重新启用
        【解决方案5】:
        static System.Windows.Forms.Timer testtimer = new System.Windows.Forms.Timer();
        
            static void Main()
            {
                testtimer.Tick += testtimertick;
                testtimer.Interval = 5000;
                testtimer.Enabled = true;
        
                while (true)
                {
                    Application.DoEvents();  //Prevents application from exiting
                }
        
            }
        
         private static void testtimertick(object sender, System.EventArgs e)
            { 
        
                    Thread t = new Thread(dostuff);
                    t.Start();
            }
        
        
        private static void dostuff()
            {
               testtimer.Enabled = false;
               //Executes some code
               testtimer.Enabled = true; //Re enables the timer but it doesn't work
               testtimer.Start();
             }
        

        【讨论】:

        • 我假设你指的是我没有使用线程计时器的事实......我只是坚持 OPs 实现虽然我同意使用线程计时器更好
        【解决方案6】:

        我刚才也遇到了类似的问题。我正在禁用计时器并在需要时再次启用。 下次我启用时,它就不起作用了。

        当我想禁用 Timer 对象时,我尝试处理它,并在每次我想启用它时创建它的新实例。不过没用。

        然后想出了一个解决方案。我正在删除在 testtimer.Tick 中配置的事件,然后在我想启用计时器时将其添加回来。

        因此,内部计时器将始终使用有效值进行实例化,并且其属性 Enabled = true。唯一的区别是,每当触发滴答事件时,它实际上不会执行任何操作。

        这将模拟禁用和启用计时器,并使其像 Enabled = false / true 一样正常工作。

        【讨论】:

          【解决方案7】:

          如果你真的想坚持使用 GUI 计时器,并从非 UI 线程启动它,你可以尝试做类似的事情,然后从非 UI 线程写入 GUI。

          我知道这不是理想的解决方案。

          this.Invoke((MethodInvoker)delegate
          {
              refreshTimer.Enabled = true;
              refreshTimer.Start();
          });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-05-10
            • 1970-01-01
            • 1970-01-01
            • 2021-12-26
            • 1970-01-01
            • 2020-01-21
            • 1970-01-01
            • 2011-09-20
            相关资源
            最近更新 更多