Sinatr
Sinatr

Reputation: 21999

Auto-dispose Threading.Timer

I want something very simple

// increment counter
Interlocked.Increment(ref _counter);

// automatically decrement counter after 1 sec
Timer timer = new Timer((o) => {
    Interlocked.Decrement(ref _counter);
    (o as Timer).Dispose();
}, timer, 1000, Timeout.Infinite);

this code, however, is not compile-able

Use of unassigned local variable 'timer'

Any easy way to fix that? It must be Threading.Timer.

P.S.: I am not sure if I must call Dispose, it's obviously un-managed resource and it's IDisposable, still on msdn they warn

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.

And I actually want it to be be collected (auto-disposed?). So, to dispose or to not dispose?

Upvotes: 6

Views: 2080

Answers (3)

Adrian Deac
Adrian Deac

Reputation: 11

If it's important that you dispose, you should try this:

class Foo {
  private Timer _timer;
  ...

  public void IncrementForASecond()
  {
    Interlocked.Increment(ref _counter);
    if(_timer != null)
      _timer.Dispose();
    _timer = new Timer((o) => {
    Interlocked.Decrement(ref _counter);
    }, null, 1000, Timeout.Infinite);
  }

This way, you are making sure that you always have a reference to the timer and that you are disposing of the previous timer before creating a new one.

Upvotes: 0

usr
usr

Reputation: 171178

There's a pattern for that:

Timer timer = null;
timer = new Timer...

Now you can use timer in the lambda body. This is basically a way to make the compiler happy while doing the same thing.

Be sure to dispose of that timer. Timers might be backed up by an OS handle resource (the CLR implementation changed a few times I believe). A timer also contains a GC handle I believe. This is harmless not to dispose of most of the time. But who knows what rare resource exhaustion you can provoke with a high volume of undisposed timers.

Doing a little code review: If you expect a cast to always work, do not use as because as documents that failure is an expected case. Not: (o as Timer). Instead: ((Timer)o).

Upvotes: 6

Marcel N.
Marcel N.

Reputation: 13976

Self disposing won't work if you don't keep a reference to it. You risk having it garbage collected, even if it's not periodical. So, it may be garbage collected before even it ticks the first time.

From the System.Threading.Timer 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.

On the other hand, it will be released via it's finalizer even if you don't dispose it (although not a good practice to rely on this). This can be seen in the Timer's source, on ReferenceSource.

Upvotes: 3

Related Questions