AFgone
AFgone

Reputation: 1250

System.Threading.Timer not firing after some time

I have a windows service application. And debugging it by running in console mode.

Here http://support.microsoft.com/kb/842793 it is written that Timers.Timer has a bug and not firing in windows services. And workaround is to use Threading.Timer And this article is for .NET 1.0 and 1.1

I am using .NET 4 but after some time Threading.Timer also doesn't fire. So what can be the reason for this? And what can you suggest as a workaround?

Upvotes: 30

Views: 18778

Answers (6)

Theodor Zoulias
Theodor Zoulias

Reputation: 43555

Here is an experimental demonstration that a non rooted System.Threading.Timer is eligible for garbage collection. The mere fact that it is scheduled for future execution, does not prevent it from being recycled.

WeakReference timer = StartTimer();
Console.WriteLine($"Before GC.Collect - IsAlive: {timer.IsAlive}");
GC.Collect();
Console.WriteLine($"After GC.Collect  - IsAlive: {timer.IsAlive}");
Thread.Sleep(1000);
Console.WriteLine($"Finished");

static WeakReference StartTimer()
{
    Timer timer = new(_ => Console.WriteLine("Callback!"), null, 500, -1);
    return new(timer);
}

Output:

Before GC.Collect - IsAlive: True
After GC.Collect  - IsAlive: False
Finished

Online demo.

Upvotes: 0

Mik
Mik

Reputation: 4414

Here is how you can use RegisterWaitForSingleObject:

When you have code registering a timer like that:

const int scheduledPeriodMilliseconds = 20000;
new Timer(ServiceBusTimerCallback, parameters, 0, scheduledPeriodMilliseconds);

private static void ServiceBusTimerCallback(object params)
{
}

You can have this:

const int scheduledPeriodMilliseconds = 20000;
var allTasksWaitHandle = new AutoResetEvent(true);

ThreadPool.RegisterWaitForSingleObject(
    allTasksWaitHandle,
    (s, b) => { ServiceBusTimerCallback(parameters); },
    null,
    scheduledPeriodMilliseconds,
    false);

You can use allTasksWaitHandle.Set() after ServiceBusTimerCallback to inform user that task was done, so it will run task again immediately. This code posted above will run task after timeont will pass, so every 20 seconds.

I implemented it in WebAPI project and it works great.

Upvotes: 2

Pedro Pereira
Pedro Pereira

Reputation: 490

Complete example of a windows service using enterprise library for logging and recurrent threads. Remove logger.write lines if not using entreprise library

namespace Example.Name.Space
{
public partial class SmsServices : ServiceBase
{
    private static String _state = "";        
    private ManualResetEvent _stop = new ManualResetEvent(false);
    private static RegisteredWaitHandle _registeredWait;
    public WindowsServices()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {                        
        Logger.Write("Starting service", LoggerCategory.Information);            
        _stop.Reset();
        _registeredWait = ThreadPool.RegisterWaitForSingleObject(_stop,
            PeriodicProcess, null, 5000, false);

    }

    protected override void OnStop()
    {            
       // UpdateTimer.Stop();
        _stop.Set();
        Logger.Write("Stopping service", LoggerCategory.Information);
    }        
    private static void PeriodicProcess(object state, bool timedOut)
    {

        if (timedOut)
        {
            // Periodic processing here
            Logger.Write("Asserting thread state", LoggerCategory.Debug);
            lock (_state)
            {
                if (_state.Equals("RUNNING"))
                {
                    Logger.Write("Thread already running", LoggerCategory.Debug);
                    return;
                }
                Logger.Write("Starting thread", LoggerCategory.Debug);
                _state = "RUNNING";
            }
            Logger.Write("Processing all messages", LoggerCategory.Information);
            //Do something 
            lock (_state)
            {
                Logger.Write("Stopping thread", LoggerCategory.Debug);
                _state = "STOPPED";
            }
        }
        else
            // Stop any more events coming along
            _registeredWait.Unregister(null);


    }
    }
}

Upvotes: 0

Grozz
Grozz

Reputation: 8425

Your timer object goes out of scope and gets erased by Garbage Collector after some time, which stops callbacks from firing.

Save reference to it in a member of class.

Upvotes: 16

atconway
atconway

Reputation: 21304

Work around?

Personally, I suggest using a RegisterWaitForSingleObject function as opposed to timers for the exact reason you are running into. The RegisterWaitForSingleObject registers a delegate to be called on interval that you set analgous to a timer and are super easy to implement. You could have a test harness up and running in a matter of hours. I use this method of interval firing in my Windows Services and it is a tried and true stable solution that works for me.

Read the link below and goto the links within the article for code samples and walkthroughs.

Running a Periodic Process in .NET using a Windows Service:
http://allen-conway-dotnet.blogspot.com/2009/12/running-periodic-process-in-net-using.html

Upvotes: 6

Jon Skeet
Jon Skeet

Reputation: 1500873

Are you keeping a reference to your timer somewhere to prevent it being garbage collected?

From the docs:

As long as you are using a Timer, you must keep a reference to it. As with any managed object, a Timer is subject to garbage collection when there are no references to it. The fact that a Timer is still active does not prevent it from being collected.

Upvotes: 68

Related Questions