2

The below class is from a .Net Windows Service. The method DoSomeDatabaseStuff takes 10 minutes when it starts the first time but when the time is elapsed this method does not get called again.

public class Test
{
        public void Start()
        {
            DoSomeDatabaseStuff();

            _oTimer = new Timer(60000);
            _oTimer.Elapsed += OnTimeout;
            _oTimer.AutoReset = true;
            _oTimer.Start();
        }

        private void OnTimeout(object source, ElapsedEventArgs e)
        {
            DoSomeDatabaseStuff();

            _oTimer = new Timer(60000);
            _oTimer.Elapsed += OnTimeout;
            _oTimer.AutoReset = true;
            _oTimer.Start();
        }
}
HatSoft
  • 11,077
  • 3
  • 28
  • 43
  • Have you tried putting a breakpoint in your `OnTimeout` method? – Dan Puzey Sep 13 '13 at 12:32
  • There is no need to reinitialize `_oTimer` within `OnTimeout` as you have `AutoReset` set to `true`. – Moo-Juice Sep 13 '13 at 12:34
  • 2
    60000 ms is one minute, not 10 ;) – Florian Gl Sep 13 '13 at 12:37
  • 1
    If all your service does is check the database every 10 minutes, then it shouldn't be a service at all. Make a simple console application that does `DoSomeDatabaseStuff`, and create a scheduled task to run it. [Computer programs are not cats](http://blog.mischel.com/2013/02/01/programs-are-not-cats/). – Jim Mischel Sep 13 '13 at 14:38

4 Answers4

6

There are lots of serious problems in this code:

  • If the Start method is meant to be the OnStart() method of the service then you can never get the service started. OnStart() must complete in less than 30 seconds. Just initialize the timer and don't do anything else
  • Creating another Timer in the Elapsed event handler is a grave mistake. Your event handler will now run twice. After the second time it is called it will run thrice. Etcetera.
  • Your test program does not test the way the code will run in a service. The Elapsed event handler will never run since the test will have been completed before your event handler can run. Which explains your observation
  • You must use try/catch in the Elapsed event handler. If you don't then any exception will be swallowed without a diagnostic. The System.Timers.Timer class is nasty like that, favor System.Threading.Timer instead Also explains your observation
  • You must ensure that your event handler is re-entrant. It can run again when the previous invocation of the event handler is still busy, this will happen when the task takes more than a minute. This very rarely comes to a good end. Setting AutoReset = false is a simple way to avoid this re-entrancy, start the timer back up at the end of the event handler to get it to repeat.
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
3

I use the System.Threading.Timer in windows-services.

Maybe this solves also your issue since others have also problems with the System.Timers.Timer in windows-services: "What I've found is that System.Timers.Timer just doesn't work in my Windows Service application. Therefore I've switched over to System.Threading.Timer"

See: Windows Service System.Timers.Timer not firing

private void InitService()
{
    //starts immediately, interval is in TimeSpan 
    this._oTimer = new System.Threading.Timer(
        OnTimeout,
        null, 
        TimeSpan.Zero,
        TimeSpan.FromMinutes(10)
    );
}

protected override void OnStart(string[] args)
{
    InitService();
}

protected override void OnStop()
{
    this._oTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}

private void ImportTimer_Elapsed(Object state)
{
    DoSomeDatabaseStuff();
}
Community
  • 1
  • 1
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
2

Your program ends before timer could run. Timer is working in background thread witch is terminated when the main thread stops.

try

static void Main(string[] args)
{
    Test t = new Test();
    t.Start();
    Console.ReadLine();
}
Dmitrii Dovgopolyi
  • 6,231
  • 2
  • 27
  • 44
1

Instead of creating a new timer every time, try setting the OnTimeout handler to:

_oTimer.Stop();
DoSomeDatabaseStuff();
_oTimer.Start();

similarly with the method Start().

JessMcintosh
  • 460
  • 2
  • 6
  • 21
  • 2
    This is not actually a fix for a re-entrancy problem. There's no guarantee that an Elapsed event handler will run when the timer's ticks, it is subject to threadpool manager scheduling delays. If the delay is long enough then a second call could have been scheduled but not executed yet. Still causing re-entrancy. Setting the timer's AutoReset property to false is the simple and correct way. – Hans Passant Sep 16 '13 at 10:16