4

I am writing a simple C# program that attempts to do something every x amount of seconds using System.Forms.Timer

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.

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();
     }
user3200174
  • 81
  • 1
  • 5
  • Just to confirm your intent here; you want the timer to trigger, then not to trigger until the `dostuff()` method has completed? – Dominic Zukiewicz Feb 04 '14 at 12:04
  • Yea exactly, the timer shouldn't start while dostuff isn't finished. – user3200174 Feb 04 '14 at 12:06
  • 1
    Have you tried `timer.Stop()`, and then `timer.Start()` when its ready? I'm also not too sure if the Timer is a thread-safe class either, so check [this post out](http://stackoverflow.com/questions/4532850/windows-forms-timer-or-system-threading-timer) – Dominic Zukiewicz Feb 04 '14 at 12:12
  • testTimer.Enabled = true; is equivalent to testTimer.start(); see: http://stackoverflow.com/questions/1012948/using-system-windows-forms-timer-start-stop-versus-enabled-true-false – TaRDy Jul 28 '14 at 17:54

7 Answers7

9

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.

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

An example of how you could use async and await with your legacy "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.
  2. The await keyword along with Task.Run was used to asynchronously run the 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.

Community
  • 1
  • 1
TaRDy
  • 999
  • 1
  • 8
  • 21
3

Don't use a GUI timer without a GUI. Don't spin with DoEvents because you are burning 100% of a CPU core with that. Use a System.Threading.Timer. It will just work.

usr
  • 168,620
  • 35
  • 240
  • 369
2

Windows Forms controls are not thread safe, you should make sure you use them on the UI thread, see e.g. C# Windows Forms Application - Updating GUI from another thread AND class?

Community
  • 1
  • 1
Grzenio
  • 35,875
  • 47
  • 158
  • 240
2

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.

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);
    }
}
Mark Hall
  • 53,938
  • 9
  • 94
  • 111
  • I think you want to change that 0 to `Timeout.Infinite`. That is `testtimer.Change(5000, Timeout.Infinite);` See http://msdn.microsoft.com/en-us/library/2x96zfy7(v=vs.110).aspx – Jim Mischel Jul 28 '14 at 19:05
  • @JimMischel actually according to the documentation, they both do the same thing. **If period is zero (0) or Infinite, and dueTime is not Infinite, the callback method is invoked once; the periodic behavior of the timer is disabled, but can be re-enabled by calling Change and specifying a positive value for period** from [MSDN](http://msdn.microsoft.com/en-us/library/yz1c7148(v=vs.110).aspx) – Mark Hall Jul 31 '14 at 23:14
1
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();
     }
apomene
  • 14,282
  • 9
  • 46
  • 72
  • I assume you are referring to the fact I am not using threading timer...I just stick to OPs implementation allthough I agree that using threading Timer is better – apomene Feb 04 '14 at 13:00
0

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. 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.

So the timer internally will be always instantiated with valid values and have its property Enabled = true. The only difference is that it won't have anything actually to perform whenever a tick event triggers.

This would imitate disabling and enabling the timer and makes it working as good as you control like Enabled = false / true.

ravi punjwani
  • 496
  • 4
  • 12
0

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.

Not the ideal solution, I know.

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