tags:

views:

127

answers:

5

I want to fire off a timer to execute once at some point in the future. I want to use a lambda expression for code brevity. So I want to do something like...

(new System.Threading.Timer(() => { DoSomething(); },
                    null,  // no state required
                    TimeSpan.FromSeconds(x), // Do it in x seconds
                    TimeSpan.FromMilliseconds(-1)); // don't repeat

I think it's pretty tidy. But in this case, the Timer object is not disposed. What is the best way to fix this? Or, should I be doing a totally different approach here?

A: 

The timer object probably implements a destructor. You can easily verify this in documentation or in the reflector.

If this is true, you shouldn't worry about it. Unless this piece of code gets called many times, in which case you should strive for deterministic deallocation of timers, meaning you would hold an array of timers, for example.

Vitaliy
+1  A: 

This will accomplish what you want, but I am not sure its the best solution. I think its something that short and elegant, but might be more confusing and difficult to follow than its worth.

System.Threading.Timer timer = null;
timer = new System.Threading.Timer(
    (object state) => { DoSomething(); timer.Dispose(); }
    , null // no state required
    ,TimeSpan.FromSeconds(x) // Do it in x seconds
    ,TimeSpan.FromMilliseconds(-1)); // don't repeat
chrisghardwick
what's the scope of timer var? if it is on the stack, when control leaves the method the timer will be disposed of. If not you need to synchronize access to it
mfeingold
+2  A: 

That approach is flawed.
You are creating an object in memory with no reference to it. This means that the timer object is available to be garbage collected. While this code will work some of the time, you cannot predict when a garbage collection will kick in and remove the timer.

For example in the code below I force a garbage collection and it causes the timer to never fire.

static void Main(string[] args)
{
    DoThing();
    GC.Collect();
    Thread.Sleep(5000);
}


static void DoThing()
{
    new System.Threading.Timer(x => { Console.WriteLine("Here"); },
            null,  
            TimeSpan.FromSeconds(1), 
            TimeSpan.FromMilliseconds(-1));
}
Matthew Manela
Correct, but strange. I would have expected that a Timer would be anchored by the Trheading-timer system somehow.
Henk Holterman
Good point. I want to do this with a similarly small amount of code but maybe I'll just have to add more code.
RichAmberale
+1  A: 

You could just wrap the timer class...

class Program
{
    static void Main(string[] args)
    {
        MyTimer.Create(
            () => { Console.WriteLine("hello"); },
            5000);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.Read();
    }
}
public class MyTimer
{
    private MyTimer() { }
    private Timer _timer;
    private ManualResetEvent _mre;

    public static void Create(Action action, int dueTime)
    {
        var timer = new MyTimer();
        timer._mre = new ManualResetEvent(false);

        timer._timer = new Timer(
            (x) =>
            {
                action();
                timer._mre.Set();
            },
            null,
            dueTime,
            Timeout.Infinite
            );

        new Thread(new ThreadStart(() =>
        {
            timer._mre.WaitOne();
            timer._timer.Dispose();
        })).Start();
    }
}
Matthew Whited
A: 

Instead of using a timer, leverage the thread pool instead:

bool fired = false;

ThreadPool.RegisterWaitForSingleObject(new ManualResetEvent(false), 
 (state, triggered) =>
 {
  fired = true;
 }, 
 0, 9000, true);

GC.Collect();

Thread.Sleep(10000);

Assert.IsTrue(fired);

This survives garbage collection since you don't have to retain a reference to anything.

Chris Patterson
And you can also cancel it by keeping a reference to the event and setting if you decide you don't want the action to run later.
Chris Patterson
You should not use the thread pool for longer tasks, particularly things that are waiting. producer/consumer would be better, or use a timer. :-)
Sam