4

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?

noctonura
  • 12,763
  • 10
  • 52
  • 85

8 Answers8

5

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
  • 16,572
  • 3
  • 64
  • 66
  • Correct, but strange. I would have expected that a Timer would be anchored by the Trheading-timer system somehow. – H H Oct 14 '09 at 20:58
  • Good point. I want to do this with a similarly small amount of code but maybe I'll just have to add more code. – noctonura Oct 14 '09 at 21:01
4

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
H H
  • 263,252
  • 30
  • 330
  • 514
chrisghardwick
  • 323
  • 1
  • 5
  • 1
    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 Oct 14 '09 at 21:19
2

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
  • 28,659
  • 3
  • 47
  • 59
  • 1
    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 Oct 14 '09 at 21:46
  • You should not use the thread pool for longer tasks, particularly things that are waiting. producer/consumer would be better, or use a timer. :-) – Samuel Neff Oct 14 '09 at 21:55
  • 1
    @Samuel Neff - Actually, RegisterWaitForSingleObject is a great fit here. The threadpool has one waiting thread for each 63 actions registered this way. See: http://msmvps.com/blogs/luisabreu/archive/2009/06/02/multithreading-registered-waits.aspx – Ohad Schneider Mar 06 '11 at 14:33
  • @ohadsc, good point, thanks for sharing, but still, this doesn't answer the original question which is how to have a timer trigger a lambda expression.. – Samuel Neff Mar 07 '11 at 05:12
  • 1
    @Samuel Neff: Indeed, no timer per se is used here, but it does answer the requirement in the title - "Best way to call a single operation at some time in the future". Personally I like it because you only have to dispose of the manual reset event, which you could share among all `RegisterWaitForSingleObject` calls. Compare that to the timer approach, where you have to dispose of it each time. See: http://blogs.msdn.com/b/morgan/archive/2008/12/18/periodic-execution-in-net.aspx – Ohad Schneider Mar 07 '11 at 08:59
  • @ohadsc, but isn't this answer really co-opting what RegisterWaitForSingleObject is for? The original poster wants to execute code based on timing. RegisterWaitForSingleObject technically can do that, but it really meant to wait for a signal, then execute code, or execute if it times out. Besides that, you mention only needing to dispose the ManualResetEvent (which this example doesn't do), but you also are supposed to call `Unregister` on the returned `RegisteredWaitHandle` (which this example is not doing either). – Samuel Neff Mar 07 '11 at 14:33
  • I see your point about co-opting, but the fact is it works well - and recommended by some smart guys (including Microsoft's Morgan Skinner). Regarding the `Unregister` call, you are absolutely correct, but it too can be encapsulated: http://stackoverflow.com/questions/2958088/built-in-background-scheduling-system-in-net/4866872#4866872 – Ohad Schneider Mar 07 '11 at 15:11
  • Actually now that I look at it, the link I gave doesn't `Unregister` the `RegisteredWaitHandle`, but it can be done in the same fashion (that is call `Unregister` inside the callback) - look at the MSDN sample: http://msdn.microsoft.com/en-us/library/system.threading.registeredwaithandle.unregister.aspx – Ohad Schneider Mar 07 '11 at 15:44
1

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
  • 22,160
  • 4
  • 52
  • 69
0

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
  • 8,044
  • 7
  • 38
  • 66
0

If you have a Dispatcher and want to be in the UI (Dispatcher) thread, use this:

    void MyNonAsyncFunction()
    {
        Dispatcher.InvokeAsync(async () =>
        {
            await Task.Delay(1000);
            MessageBox.Show("Thank you for waiting");
        });
    }

This function is not async because you did not want to wait within your function. This approach might be useful if you wanted to schedule more than one events at different times, but perhaps you really want the approach below:

    async void MyAsyncFunction()
    {
        // Do my other things

        await Task.Delay(1000);
        MessageBox.Show("Thank you for waiting");
    }

Which does the same thing, but requires the await to happen at the end of your function.

Since you may not have a Dispatcher or want to use it, but still want to schedule multiple operations at different times, I would use a thread:

    static void MyFunction()
    {
        // Do other things...
        Schedule(1000, delegate
        {
            System.Diagnostics.Debug.WriteLine("Thanks for waiting");
        });
    }

    static void Schedule(int delayMs, Action action)
    {
#if DONT_USE_THREADPOOL
        // If use of threadpool is undesired:
        new System.Threading.Thread(async () =>
        {
            await Task.Delay(delayMs);
            action();
        }
        ).Start(); // No need to store the thread object, just fire and forget
#else
        // Using the threadpool:
        Task.Run(async delegate
        {
            await Task.Delay(delayMs);
            action();
        });
#endif
    }

If you want to avoid async, I would recommend not using the threadpool and replacing the await Task.Delay(delayMs) call with a Thread.Sleep(delayMs) call

John Thoits
  • 355
  • 1
  • 12
  • 1
    The first approach only works if there is actually a running dispatcher available (e.g. manually started, or inside a WPF application) – Ehssan Dec 02 '16 at 11:10
  • @Ehssan That's true, and the OP didn't specify whether this was UI or not. I still would prefer to avoid using a Timer though, as that's designed for a different problem. I'll add a third option that uses a thread. – John Thoits Dec 19 '16 at 22:21
0

You could use TaskCompletionSource for example:

static Task<T> ExecuteLater<T>(int delay, Func<T> func)
{
    var tcs = new TaskCompletionSource<T>();

    var timer = new System.Timers.Timer(delay) { AutoReset = false };
    timer.Elapsed += delegate { timer.Dispose(); tcs.SetResult(func()); };
    timer.Start();

    return tcs.Task;
}

and call it like:

var result = await ExecuteLater<int>(5000, () => 50);

Or simply call:
 var result = await Task.Delay(5000).ContinueWith<int>((t) => { return 50; });
Legends
  • 21,202
  • 16
  • 97
  • 123
-2
          System.Reactive.Linq.Observable.Interval(TimeSpan.FromSeconds(1))
            .FirstAsync()
            .Subscribe(_ => DoSomething()));
felix
  • 97
  • 5
  • 2
    Please do not post bare code as an answer. Add some commentary on what the code is doing. – Jonathan Mee Dec 02 '16 at 12:32
  • Please use the [edit] link explain how this code works and don’t just give the code, as an explanation is more likely to help future readers – Jed Fox Dec 02 '16 at 18:45