65

I have a method which should be delayed from running for a specified amount of time.

Should I use

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

Or

Timer timer = new Timer(o => action(), null, millisecond, -1);

I had read some articles about how using Thread.Sleep is bad design. But I don't really understand why.

However, for using Timer, Timer has a dispose method. Since the execution is delayed, I don't know how to dispose the Timer. Do you have any suggestions?

Or, if you have an alternative suggestion for delaying code execution, that would also be appreciated.

TarHalda
  • 1,050
  • 1
  • 9
  • 27

6 Answers6

48

One difference is that System.Threading.Timer dispatches the callback on a thread pool thread, rather than creating a new thread every time. If you need this to happen more than once during the life of your application, this will save the overhead of creating and destroying a bunch of threads (a process which is very resource intensive, as the article you reference points out), since it will just reuse threads in the pool, and if you will have more than one timer going at once it means you will have fewer threads running at once (also saving considerable resources).

In other words, Timer is going to be much more efficient. It also may be more accurate, since Thread.Sleep is only guaranteed to wait at LEAST as long as the amount of time you specify (the OS may put it to sleep for much longer). Granted, Timer is still not going to be exactly accurate, but the intent is to fire the callback as close to the specified time as possible, whereas this is NOT necessarily the intent of Thread.Sleep.

As for destroying the Timer, the callback can accept a parameter, so you may be able to pass the Timer itself as the parameter and call Dispose in the callback (though I haven't tried this -- I guess it is possible that the Timer might be locked during the callback).

Edit: No, I guess you can't do this, since you have to specify the callback parameter in the Timer constructor itself.

Maybe something like this? (Again, haven't actually tried it)

class TimerState
{
    public Timer Timer;
}

...and to start the timer:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

The locking should prevent the timer callback from trying to free the timer prior to the Timer field having been set.


Addendum: As the commenter pointed out, if action() does something with the UI, then using a System.Windows.Forms.Timer is probably a better bet, since it will run the callback on the UI thread. However, if this is not the case, and it's down to Thread.Sleep vs. Threading.Timer, Threading.Timer is the way to go.

T.S.
  • 18,195
  • 11
  • 58
  • 78
Eric Rosenberger
  • 8,987
  • 1
  • 23
  • 24
  • 4
    It's also worth pointing out the difference with System.Windows.Forms.Timer, which I *believe* calls a function on the UI thread, which is really important for WinForms apps! – Dave Markle Dec 24 '08 at 17:13
  • 2
    For future readers, `Sleep` isn't event guaranteed to be AT LEAST, it's documented that it could be less. – Peter Ritchie Apr 01 '13 at 14:04
  • 4
    Out of curiosity... where is that documented? – Eric Rosenberger Apr 01 '13 at 15:05
  • I tried this and got a out of memory message in 5 minutes – software is fun Mar 12 '15 at 12:46
  • @EricRosenberger - I don't know if you are still interested in this, but I came across this thread and found your comment. I have answered in three parts since it's too long to fit on one comment. – Wai Ha Lee Apr 13 '15 at 10:23
  • 1
    The MSDN documentation for [Thread.Sleep(Int32)](http://msdn.microsoft.com/en-us/library/d00bd51t) says: "*The actual timeout might not be exactly the specified timeout, because the specified timeout will be adjusted to coincide with clock ticks. For more information on clock resolution and the waiting time, see the [Sleep function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298) topic. This method calls the [Sleep function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298) from the Windows system APIs.*". – Wai Ha Lee Apr 13 '15 at 10:24
  • 2
    The "*Sleep Function*" documentation says: "*This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds. The system clock "ticks" at a constant rate. If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on.*" – Wai Ha Lee Apr 13 '15 at 10:24
  • 1
    One of the most disturbing things I've seen in C# is that you CAN do the thing you say you can't do. The timer constructor can receive an action which closes over the timer itself, by referencing the local variable the constructor gets assigned to. – Paul K Jan 29 '20 at 15:21
19

use ThreadPool.RegisterWaitForSingleObject instead of timer:

//Wait 5 seconds then print out to console. 
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);
Cédric Bignon
  • 12,892
  • 3
  • 39
  • 51
  • Read the accepted answer. Timer already uses the thread pool. – John Saunders Jul 30 '09 at 23:53
  • 14
    I prefer RegisterWaitForSingleObject for the logic... The method is executed ONCE when the time is up... so you dont have to trick to stop the timer once the job is done that is not good... so RegisterWaitForSingleObject naturaly do exactly what he want, timer dont timer is better when you want execute a task several times at specific intervals.... –  Jul 31 '09 at 21:36
  • I added an example. More details available at this question http://stackoverflow.com/questions/1535262/net-timeouts-waitforsingleobject-vs-timer – Greg Bray May 25 '12 at 23:06
  • Also http://msdn.microsoft.com/en-us/library/0ka9477y(v=vs.80).aspx#sectionSection4 – Greg Bray May 25 '12 at 23:25
  • I agree and this is the method I prefer. The following post I wrote supports this answer and elaborates on some of the uses for those wondering how to use RegisterWaitForSingleObject: http://allen-conway-dotnet.blogspot.com/2009/12/running-periodic-process-in-net-using.html – atconway Nov 20 '12 at 21:58
16

I think Thread.Sleep is fine if you really want to pause the application for a specified amount of time. I think the reason people say it is a bad design is because in most situations people don't actually want the application to pause.

For example, I was working on a pop3 client where the programmer was using Thread.Sleep(1000) to wait while the socket retrieved mail. In that situation it was better to hook up an event handler to the socket and continuing program execution after the socket had completed.

Shawn
  • 19,465
  • 20
  • 98
  • 152
  • 3
    Except in neither example in the post is the application actually pausing. The poster is doing the pause on a separate thread, not calling Thread.Sleep directly. Calling Thread.Sleep directly, yes, bad idea. – Eric Rosenberger Dec 24 '08 at 17:23
3

I remember implementing a solution similar to Eric's one. This is however a working one ;)

class OneTimer
    {
        // Created by Roy Feintuch 2009
        // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
        public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
        {
            var td = new TimerDisposer();
            var timer = new Timer(myTdToKill =>
            {
                try
                {
                    cb();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
                }
                finally
                {
                    ((TimerDisposer)myTdToKill).InternalTimer.Dispose();
                }
            },
                        td, dueTime, TimeSpan.FromMilliseconds(-1));

            td.InternalTimer = timer;
        }
    }

    class TimerDisposer
    {
        public Timer InternalTimer { get; set; }
    }
Froyke
  • 41
  • 2
2

The only beef that I have with the System.Timer is that most of the time I have seen it used for long delays (hours, minutes) in polling services and developers often forget to launch the event Before they start the timer. This means that if I start the app or service, I have to wait until the timer elapses (hours, minutes) before it actually executes.

Sure, this is not a problem with the timer, but I think that its often used improperly by because its just too easy to misuse.

StingyJack
  • 19,041
  • 10
  • 63
  • 122
1

@miniscalope No don't use ThreadPool.RegisterWaitForSingleObject instead of timer, System.Threading.Timer will queue a callback to be executed on a thread pool thread when the time has elapsed and doesn't require a wait handle, wait for single object will tie up a threadpool thread waiting for the event to be signalled or the timeout to expire before the thread calls the callback.

Matt
  • 2,984
  • 1
  • 24
  • 31
  • Do you have some documentation on this? Are you essentially saying `ThreadPool.RegisterWaitForSingleObject` works like `Thread.Sleep`by stating: *"wait for single object will tie up a threadpool thread waiting for the event to be signaled or the timeout to expire before the thread calls the callback"*? – atconway Nov 20 '12 at 22:01
  • It doesn't use Thread.Sleep... after digging through the source it implies that it calls the native RegisterWaitForSingleObject (I say implies because the method it calls is extern but decorated as an internal runtime call...) If that assumption is correct, you can assume (from the [docs](http://msdn.microsoft.com/en-us/library/windows/desktop/ms685061(v=vs.85).aspx)) that it will use a wait thread from the native threadpool to wait for the object to be signalled, then executes the callback on a worker thread. – Matt Nov 22 '12 at 00:21