Compare using Thread.Sleep and Timer for delayed execution
Asked Answered
B

6

66

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.

Beseech answered 24/12, 2008 at 15:4 Comment(0)
C
49

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.

Cashew answered 24/12, 2008 at 16:9 Comment(8)
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!Vas
For future readers, Sleep isn't event guaranteed to be AT LEAST, it's documented that it could be less.Billiards
Out of curiosity... where is that documented?Cashew
I tried this and got a out of memory message in 5 minutesKirstiekirstin
@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.Fredericafrederich
The MSDN documentation for Thread.Sleep(Int32) 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 topic. This method calls the Sleep function from the Windows system APIs.".Fredericafrederich
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."Fredericafrederich
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.Floccus
B
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);
Bufordbug answered 30/7, 2009 at 23:51 Comment(5)
Read the accepted answer. Timer already uses the thread pool.Starlastarlene
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....Bufordbug
I added an example. More details available at this question #1535762Microbicide
Also msdn.microsoft.com/en-us/library/…Microbicide
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: allen-conway-dotnet.blogspot.com/2009/12/…Trackandfield
S
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.

Scholem answered 24/12, 2008 at 15:16 Comment(1)
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.Cashew
C
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; }
    }
Chem answered 22/7, 2010 at 6:41 Comment(0)
D
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.

Driftwood answered 24/12, 2008 at 17:9 Comment(0)
I
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.

Iterate answered 31/7, 2009 at 1:30 Comment(2)
Do you have some documentation on this? Are you essentially saying ThreadPool.RegisterWaitForSingleObject works like Thread.Sleepby 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"?Trackandfield
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) 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.Iterate

© 2022 - 2024 — McMap. All rights reserved.