0
        var startTimeSpan = TimeSpan.Zero;
        var periodTimeSpan = TimeSpan.FromSeconds(5);

        var timer = new System.Threading.Timer((e) =>
        {
            limit = helper.CheckLimit();
        }, null, startTimeSpan, periodTimeSpan); 

In my C# application, the timers working only 30 times. After that stops. Why, I dont know. Where is the wrong?

This is my CheckLimit helper;

  public bool CheckLimit()
    {
        try
        {
            var queryParams = new NameValueCollection()
            {
                { "p", "check_limit" },
            };

            string response = this.ApiCall(queryParams);
            dynamic json_response = JsonConvert.DeserializeObject(response);
            if (json_response.success == true)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        catch (Exception)
        {
            return false;
        }
    }
Nusret
  • 17
  • 8
  • Can you share the surrounding code? specifically `helper.CheckLimit()` – Dortimer Oct 11 '19 at 18:01
  • I shared, thanks . – Nusret Oct 11 '19 at 18:03
  • Where is `var timer` located? In some method? – GSerg Oct 11 '19 at 18:03
  • I dont understand, Can you explain? – Nusret Oct 11 '19 at 18:09
  • 6
    Are you keeping a reference to the timer alive, to make sure it isn't garbage collected? From the documentation: 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. – Jon Skeet Oct 11 '19 at 18:16
  • 1
    How do you know that the timer has stopped working? Do you have some code that's checking to make sure that the callback is actually called? – Jim Mischel Oct 11 '19 at 20:06
  • @JonSkeet I learned today that the [public Timer(TimerCallback callback)](https://referencesource.microsoft.com/#mscorlib/system/threading/timer.cs,919) uses `this` for `state` and will survive garbage collection. (I have an example below) – tymtam Oct 12 '19 at 12:14

1 Answers1

0

My bet is with Jon's suggestion which I shall plagiarise into an answer build upon.

The doco for Timer Class (System.Threading) contains the following note.

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.

var timer suggests that you may not be keeping a reference alive. You could make it a field of an object which stays alive throughout the app's lifetime.

Advanced note: new System.Threading.Timer(TimerCallback callback) constructor uses this for the state object and this keeps the timer alive throughout GC.Collects (please see below for more).


Example

Here is a complete example that illustrates the problem.

Output

A
B
A
B
B
A
GC Collected
B
B
B

Code

using System;
using System.Threading.Tasks;

class TimerExperiment
{
    System.Threading.Timer timer; 
    public TimerExperiment() {
        StartTimer("A"); // Not keeping this timer
        timer = StartTimer("B"); // Keeping this timer
    }

    static System.Threading.Timer StartTimer(string name) {
        return new System.Threading.Timer(_ =>
        {
            Console.WriteLine($"{name}");
        }, null, dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(1));
    }

}

class Program
{
    static async Task Main(string[] args)
    {
        var withTimers = new TimerExperiment();

        await Task.Delay(TimeSpan.FromSeconds(2));
        GC.Collect();
        Console.WriteLine("GC Collected");
        Console.ReadLine();
    }
}


Muddying the waters

System.Threading.Timer's Timer(TimerCallback callback) constructor (the one that doesn't take the dueTime and others) uses this for state which means that the timer will keep a reference to itself and this means that it will survive garbage collection.

public Timer(TimerCallback callback)
{
    (...)
    TimerSetup(callback, this, (UInt32)dueTime, (UInt32)period, ref stackMark);
}

Example 2

Timer 'C' is created using Timer(TimerCallback callback) and it just keeps going.

Output

A
B
C
A
B
C
GC Collected
B
C
C
B
B
C

Code

class TimerExperiment
{
    System.Threading.Timer timerB;

    public TimerExperiment()
    {
        StartTimer("A"); // Not keeping this timer
        timerB = StartTimer("B"); // Keeping this timer
        StartTimer2("C"); // Not keeping this timer
    }

    static System.Threading.Timer StartTimer(string name) {
        return new System.Threading.Timer(_ =>
        {
            Console.WriteLine($"{name}");
        }, null, dueTime: Delay(name), period: TimeSpan.FromSeconds(1));
    }

    static System.Threading.Timer StartTimer2(string name)
    {
        //Create the timer using the constructor which only takes the callback
        var t = new System.Threading.Timer( _ => Console.WriteLine($"{name}"));
        t.Change(dueTime: Delay(name), period: TimeSpan.FromSeconds(1));
        return t;
    }

    static TimeSpan Delay(string name) 
            => TimeSpan.FromMilliseconds(Convert.ToInt64(name[0])*10);

}

class Program
{
    static async Task Main(string[] args)
    {
        var withTimers = new TimerExperiment();

        await Task.Delay(TimeSpan.FromSeconds(2));
        GC.Collect();
        Console.WriteLine("GC Collected");
        Console.ReadLine();
    }
}
tymtam
  • 31,798
  • 8
  • 86
  • 126