简体   繁体   English

计时器在禁用后不想再次启动

[英]Timer doesn't want to start again after it was disabled

I am writing a simple C# program that attempts to do something every x amount of seconds using System.Forms.Timer 我正在编写一个简单的C#程序,尝试使用System.Forms.Timer每隔x秒执行一次操作

The tick event calls a method that starts a new thread and disables the timer, then when the thread is done with its work, it enables the timer again, but the problem is, now it doesn't tick after it's been enabled. 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();
     }

As @grzenio said , it appears that your issue has to do with the fact that you are making cross thread calls to a Windows Form Control that was created on a different thread. 正如@grzenio所说 ,您的问题似乎与您正在对在不同线程上创建的Windows窗体控件进行跨线程调用这一事实有关。

If you are using .NET 4.5 (C# 5.0), I would suggest looking at the async/await keywords , a good introduction can be found at Stephen Cleary's Blog 如果您使用的是.NET 4.5(C#5.0),我建议您查看async / await关键字 ,可以在Stephen Cleary的博客中找到一个很好的介绍

An example of how you could use async and await with your legacy "DoStuff": 您可以如何使用异步和等待旧版“DoStuff”的示例:

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

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

        _Timer.Enabled = true;
    }

Things to notice: 需要注意的事项:

  1. async was added to the Timer_Tick event's signature. 异步被添加到Timer_Tick事件的签名中。
  2. The await keyword along with Task.Run was used to asynchronously run the DoStuff. await关键字和Task.Run一起用于异步运行DoStuff。

When using these keywords, the DoStuff will be run asynchronously and once DoStuff returns, it will continue on the line after await using the context of the thread that originally called Tick. 当使用这些关键字时,DoStuff将以异步方式运行,一旦DoStuff返回,它将在等待使用最初调用Tick的线程的上下文之后继续行。

Don't use a GUI timer without a GUI. 不使用GUI的GUI计时器。 Don't spin with DoEvents because you are burning 100% of a CPU core with that. 不要使用DoEvents旋转,因为你正在使用它来燃烧100%的CPU核心。 Use a System.Threading.Timer . 使用System.Threading.Timer It will just work. 它会起作用。

You can use System.Threading.Timer to do what you want to do, using the Change Method to set the time and the Period, Just restart it when you finish your work. 您可以使用System.Threading.Timer执行您想要执行的操作,使用Change Method设置时间和Period,只需在完成工作后重新启动它。

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

Windows窗体控件不是线程安全的,您应该确保在UI线程上使用它们,请参阅例如C#Windows窗体应用程序 - 从另一个线程AND类更新GUI?

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

I had a similar issue just now. 我刚才有类似的问题。 I was disabling the timer and enabling again whenever I want. 我正在禁用计时器并在需要时再次启用。 The next time when I enable, it won't work. 下次启用时,它将无法工作。

I tried disposing the Timer object when I want to disable and creating new instance of it each time I want to enable it. 我想在每次要启用时禁用和创建新实例时尝试处理Timer对象。 Didn't work though. 虽然没有工作。

Figured out a solution then. 然后想出一个解决方案。 I'm removing the event which is configured in testtimer.Tick, and then adding it back when I want to enable the timer. 我正在删除在testtimer.Tick中配置的事件,然后在我想启用计时器时将其添加回来。

So the timer internally will be always instantiated with valid values and have its property Enabled = true. 因此,内部的计时器将始终使用有效值进行实例化,并使其属性Enabled = true。 The only difference is that it won't have anything actually to perform whenever a tick event triggers. 唯一的区别是,只要tick事件触发,它就不会有任何实际执行的东西。

This would imitate disabling and enabling the timer and makes it working as good as you control like Enabled = false / true. 这将模仿禁用和启用计时器,并使其像您控制的那样工作,如Enabled = false / true。

If you really want to stick to the GUI timer, and start it from non UI thread, you can try to do similar stuff, then write to GUI from non UI thread. 如果你真的想坚持GUI计时器,并从非UI线程启动它,你可以尝试做类似的事情,然后从非UI线程写入GUI。

Not the ideal solution, I know. 我知道,这不是理想的解决方案。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM