Manachi
Manachi

Reputation: 1064

Windows Service not starting in a timely manner - pass off initial workload to timer?

I'm running a Windows Service which does a large data migration process (will take 10 minutes+ on first run through). Currently I have the service running once initially on startup, and then every 4 hours thereafter. Something like this (excerpt):

protected override void OnStart(string[] args)
{
    // Set up timer
    int intervalSync = 14400; // 4 hours default
    timer = new System.Timers.Timer();
    timer.Interval = intervalSync * 1000;
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimer);
    timer.Start();

    // Call once initially.
    this.DoMigration();
}

public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
{
    this.DoMigration();
}

The problem is after deploying and running, the Windows Service errors with "The service did not respond to the start or control request in a timely fashion".

I realise I could just remove the initial call of DoMigration() from OnStart, and let the service start up quickly - but then the work would not be done for 4 hours which is not desirable.

Is there a way I can set a different time interval for the first timer to run (perhaps 30 seconds or 60 seconds), and then the separate 4 hour periodic interval after that? Basically I want to push the workload out of OnStart to prevent the error, but I want to still have it run as soon as possible after starting.

Is this possible? And is it the best approach? Any advice would be appreciated.

Upvotes: 0

Views: 494

Answers (2)

Peter Duniho
Peter Duniho

Reputation: 70701

IMHO, the easiest way to address this is to use async/await:

protected override void OnStart(string[] args)
{
    // Storing the returned Task in a variable is a hack
    // to suppresses the usual compiler warning.
    var _ = DoMigrationLoop();
}

private async Task DoMigrationLoop()
{
    while (true)
    {
        await Task.Run(() => this.DoMigration());
        await Task.Delay(TimeSpan.FromHours(4));
    }
}

This causes the DoMigration() method to be executed in a separate thread immediately, and then every four hours thereafter. Note that since the original DoMigrationLoop() method call returns immediately at the first await call, the OnStart() method is itself not delayed at all.

Feel free to add task cancellation, exception handling, and other niceties as desired. :)

Upvotes: 1

DavidG
DavidG

Reputation: 119066

The problem is that the OnStart method needs to complete in a fairly short time (30 seconds perhaps?) before Windows assumes something has gone wrong. A simple solution would be to fire the timer very quickly on it's first run, the reconfigure the timer from inside the event to run again in 4 hours:

protected override void OnStart(string[] args)
{
    // Set up timer
    int initialIntervalSync = 10; // 10 seconds
    timer = new System.Timers.Timer();
    timer.Interval = initialIntervalSync * 1000;
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimer);
    timer.Start();    
}

public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
{
    //Reset timer interval
    int intervalSync = 14400; // 4 hours default
    timer.Interval = intervalSync * 1000;

    this.DoMigration();
}

Upvotes: 1

Related Questions