Why is Thread.Sleep so harmful
Asked Answered
T

9

156

I often see it mentioned that Thread.Sleep(); should not be used, but I can't understand why this is so. If Thread.Sleep(); can cause trouble, are there any alternative solutions with the same result that would be safe?

eg.

while(true)
{
    doSomework();
    i++;
    Thread.Sleep(5000);
}

another one is:

while (true)
{
    string[] images = Directory.GetFiles(@"C:\Dir", "*.png");

    foreach (string image in images)
    {
        this.Invoke(() => this.Enabled = true);
        pictureBox1.Image = new Bitmap(image);
        Thread.Sleep(1000);
    }
}
Tarlatan answered 11/1, 2012 at 7:52 Comment(11)
Can you point to specific references? Anecdotal evidence does not support an argument.Pedestrian
A summary of the blog might be 'don't misuse Thread.sleep()'.Bathilda
I wouldn't say it's harmful. I would rather say that it's like goto: i.e. there is probably a better solution to your problems than Sleep.Sipple
It's not exactly the same as goto, which is more like a code smell than a design smell. There's nothing wrong with a compiler inserting gotos into your code: the computer doesn't get confused. But Thread.Sleep isn't exactly the same; compilers don't insert that call and it has other negative consequences. But yes, the general sentiment that using it is wrong because there's almost always a better solution is certainly correct.Kwangchow
Sleep has its uses but has been misused* and abused a lot (extreme 1). I guess that it is easier for some to try to simply ban it (extreme 2/the other one). That being said, a combination of thread pools + work items + timers/events will give you more performance at the cost of extra coding time/lines of code (instead of sleeping, that thread could do something else without you having to spawn another one). Misuse: just try to run any of those samples in a GUI app, on main/UI thread -> that makes Sleep as harmful as dynamite when trying to open a door but hey, why did you choose dynamite?Secretariat
Why can't thread.sleep be used in a long-running server or gui app that needs to periodically check io-requests,etc?Benignant
@KevinKostlan use a timer or scheduling framework (like quartz.net) then...Planetstruck
Everyone is providing opinions on why the above examples are bad, but noone has provided a rewritten version that does not use Thread.Sleep() that still accomplishes the goal of the given examples.Gallonage
Answers here a bit old. Today use Task.Delay. See is-it-always-bad-to-use-thread-sleepHegyera
This would also be a good read Thread.Sleep (blocking) vs Task.Delay (non-blocking)Tallie
I didnt understand why it is so harmful!? And, could we see a code example on for instance, how to do a retry after 3 seconds of a Web API call the correct way?Pregnable
P
188

The problems with calling Thread.Sleep are explained quite succinctly here:

Thread.Sleep has its use: simulating lengthy operations while testing/debugging on an MTA thread. In .NET there's no other reason to use it.

Thread.Sleep(n) means block the current thread for at least the number of timeslices (or thread quantums) that can occur within n milliseconds. The length of a timeslice is different on different versions/types of Windows and different processors and generally ranges from 15 to 30 milliseconds. This means the thread is almost guaranteed to block for more than n milliseconds. The likelihood that your thread will re-awaken exactly after n milliseconds is about as impossible as impossible can be. So, Thread.Sleep is pointless for timing.

Threads are a limited resource, they take approximately 200,000 cycles to create and about 100,000 cycles to destroy. By default they reserve 1 megabyte of virtual memory for its stack and use 2,000-8,000 cycles for each context switch. This makes any waiting thread a huge waste.

The preferred solution: WaitHandles

The most-made-mistake is using Thread.Sleep with a while-construct (demo and answer, nice blog-entry)

EDIT:
I would like to enhance my answer:

We have 2 different use-cases:

  1. We are waiting because we know a specific timespan when we should continue (use Thread.Sleep, System.Threading.Timer or alikes)

  2. We are waiting because some condition changes some time ... keyword(s) is/are some time! if the condition-check is in our code-domain, we should use WaitHandles - otherwise the external component should provide some kind of hooks ... if it doesn't its design is bad!

My answer mainly covers use-case 2

Planetstruck answered 11/1, 2012 at 7:57 Comment(27)
I wouldn't call 1 MB of memory a huge waste considering todays hardwareSipple
@Default hey, discuss that with the original author :) and, it always depends on your code - or better: the factor ... and the main-issue is "Threads are a limited resource" - todays kids don't know much about effeciency and cost of certain implementations, because "hardware is cheap" ... but sometimes you need to code very optdPlanetstruck
Surely, using a WaitHandle would use more resources than a sleep() call?Bathilda
@MartinJames well, WaitHandle goes to native and back (according to yoda.arachsys.com/csharp/threads/waithandles.shtml) ... atm i do not know what Thread.Sleep does, but both call external-win32-methods ... so some guy with a win32-background should answer that!Planetstruck
@MartinJames I assume that most people use a while (condition) Thread.Sleep(1000), or even better while (condition);, construct ... this is design-smell and should be optd with a WaitHandle anyways!Planetstruck
'This makes any waiting thread a huge waste' eh? If some protocol spec demands a one-second pause before continuing, what is going to wait for 1 second? Some thread, somewhere, is going to have to wait! The overhead for thread create/destroy is often irrelevant because a thread has to be raised anyway for other reasons and it runs for the lifetime of the process. I would be interested to see any way of avoiding context-switches when a spec says 'after turning on the pump, wait at least ten seconds for the pressure to stabilize before opening the feed valve'.Bathilda
Right, if a protocol spec demands waiting, then spinning and waiting might be the right thing to do. But generally, that's not the case. This is a red herring argument.Kwangchow
@MartinJames summary: we have 2 different use-cases: 1) we are waiting because we know a specific timeSpan when we should continue (use Thread.Sleep or alikes) 2) we are waiting because some condition changes some time ... keyword(s) some time! if the condition-check is in our code-domain, we should use WaitHandles - otherwise the external component should provide some kind of hooks ... if it doesn't its design is bad! My answer mainly covers use-case 2 ...Planetstruck
@AndreasNiedermair it is beyond argument that sleep() can, and often is, used as a latency-ridden, resource-wasting piss-poor inter-thread comms mechanism that can be replaced with more effective signalling. The only issue I have is with the conclusion that sleep() should never be used except in some restricted testing role.Bathilda
@MartinJames :) pal, i like you ... anyway, i've tried to cover this in my edit - unfortunately i can't (and don't want to) edit the quote, as it is based on a blog-entry - so editing my quote would be quite wired :) - it's not my conclusion, it's the original authors ... i've included my conclusion as en edit, which covers your plea!Planetstruck
@CodyGray - I read the post again. I see no fish of any colour in my comments. Andreas pulled out of the web: 'Thread.Sleep has it's use: simulating lengthy operations while testing/debugging on an MTA thread. In .NET there's no other reason to use it'. I argue that there are many apps where a sleep() call is, well, just what is required. If leigons of developers, (for there are many), insist on using sleep() loops as condition monitors that should be replaced with events/condvars/semas/whatever, that's no justification for an assertion that 'there's no other reason to use it'.Bathilda
@AndreasNiedermair - in response to your edit, yes, I agree 100% - such misuses of sleep() should be avoided.Bathilda
@MartinJames hehe, i'm getting a hint to "create a discussion chat" - besides that: i would say, that apps (in the enterprise environment), which require some sleep-mechanism, are mostly heavily dependend on real-time-processing and/or direct hardware-usage. So either Thread.Sleep or C# is not the best way to implement such a scenario (but maybe a comfortable one :) )!Planetstruck
The article you link to is inaccurate when it says "There is one non-breaking use for Thread.Sleep: Thread.Sleep(0). This tells the system you want to forfeit the rest of the thread's timeslice and let another, waiting, thread run.". Thread.Sleep(0) will only cede to threads of equal or greater priority, Thread.Sleep(1) is needed to cede to any waiting thread. On a single-CPU machine it's more than possible for something calling just Thread.Sleep(0) in what is intended to be a short tight spin to wait 3secs for the low-priority thread its waiting on to be boosted.Giliana
@JonHanna actually the author elaborates his entry in a comment ("Saturday, November 14, 2009 7:56 AM by PeterRitchie"), where he makes it obvious that he was not after thread-priorization in his article ...Planetstruck
@AndreasNiedermair Fair enough, I read some but not all of the comments. Of course, changing priorities of threads is a stronger case than Thread.Sleep of has-its-place-but-most-uses-are-a-mistake, but if you're writing something that doesn't completely control every thread it could possibly see then it's an important case to consider. If you have to spin tight (not in itself something one should do often) then Thread.Sleep(0) now and again and throw in the odd Thread.Sleep(1) if you've only one core to use.Giliana
In 30 years of multiThreaded app development, (mostly C++/Delphi/Windows), I have never seen any need for sleep(0) or sleep(1) loops in any deliverable code. Occasionally, I have shoved in such code for debugging purposes, but it's never made its way to the customer. 'if you're writing something that doesn't completely control every thread' - micro-management of threads is as big a mistake as micro-management of development staff. Thread management is what the OS is there for - the tools it provides should be used.Bathilda
The one sane use case I know of for Thread.Sleep is having a fixed-rate update loop, like a game where you want a consistent FPS. If there's time to kill before the next frame, use a low-balled Thread.Sleep, maybe Thread.Sleep(1), to give some of it back to the system.Iinden
"This means the thread is almost guaranteed to block for more than n milliseconds. " -- This is absolutely not the case. The only time it would be guaranteed to block for 15+ ms would be if there were actually another thread there to consume those 15ms. If all the other threads yield, then a sleep(0) or sleep(1) MIGHT come back sooner... particularly on a CPU with lots of cores. There are now also higher performance thread options that you can take advantage of that guarantee 1ms time slices if used properly (for audio threads and the like).Zaid
@JasonNelson Actually, the claim is still true ... But the concrete resolution strongly depends on the OS (and whether you are on a consumer or server version) and the implementation of the native scheduler and the time between context switches (which is somewhat connected to consumer vs server) - you can't get any better than that resolution ... Joe Duffy covers this very issue in his book "Concurrent Programming on Windows" on page 163ff "Quantums". Please also see https://mcmap.net/q/75780/-thread-quantumPlanetstruck
@JasonNelson forums.guru3d.com/… and microsoftpressstore.com/articles/… have some more information on this topic.Planetstruck
@AndreasNiedermair well... I just realized I quoted the wrong line in my "quote".... my apologies for the confusion... I meant to dispute the statement that have to wait for the ENTIRE timeslice before the thread wakes up again. A Sleep(0) will usually return immediately if there are no other threads taking up the time-slices. A lot depends on the OS, yes it is true, but even the piddly-little FreeRTOS that I use for Embedded systems won't make me wait a whole timeslice when there are no other active threads. If thread schedulers operated that way... they'd take a year to boot.Zaid
@JasonNelson thanks for the clarification. I have to be honest w/ you: My answer covers a Windows-only scenario, where it is nearly impossible to have the same or less amount of threads as available cores (w/ HT) in a fully booted OS - therefore the additional time, due to context-switches, is more or less inevitable. Finally, I hope that the original quote is explicit enough, as it IMO clearly states that the additional time comes from the context switches, not from the quantums itself. If you feel the need for additional clarification, I'd be happy to adapt my answer accordingly! Let me knowPlanetstruck
@AndreasNiedermair - I downvoted you a few years ago, because Its hard to give weight to your answer without an example other than "dont do that". I have to use Thread.Sleep(1) in continuous worker loops ("check for work, do work, repeat"), that may not have work to do every cycle. Without it, the entire cpu core is used 100%.Gallonage
Considering the small eco systems like mobile/wearable/auto/small-devices, 1MB is very huge memory. We should handle the threads very carefully. In such ecosystems, applications reach memory limits very easily.Rathbone
@Gallonage No you don't have to. There are better solutions, WaitHandle/Async(Task), etc. Thread.Sleep(1) is just a poor one. Think about when you talking about bad game optimization vs great optimization, those "tiny" drawbacks can add up and your product starts to lose its competencies.Fortyfive
ok @Fortyfive a practical and real example;. you have a windows service that needs to perform some work (not necessarily a Task) every N minutes. You cant Thread.Sleep for N minutes, because any more than 30 seconds makes the service unresponsive to the SCM and is unable to stop or shut down (users: "program is broken!"),. Timers have the same problems as well as having the problem of getting orphaned in a service and also with having to enforce a singleton execution. What other choices are there aside from (check time -> if time, run -> sleep 1 ms)?Gallonage
W
38

SCENARIO 1 - wait for async task completion: I agree that WaitHandle/Auto|ManualResetEvent should be used in scenario where a thread is waiting for task on another thread to complete.

SCENARIO 2 - timing while loop: However, as a crude timing mechanism (while+Thread.Sleep) is perfectly fine for 99% of applications which does NOT require knowing exactly when the blocked Thread should "wake up*. The argument that it takes 200k cycles to create the thread is also invalid - the timing loop thread needs be created anyway and 200k cycles is just another big number (tell me how many cycles to open a file/socket/db calls?).

So if while+Thread.Sleep works, why complicate things? Only syntax lawyers would, be practical!

Wingding answered 18/2, 2013 at 16:46 Comment(2)
Thankfully we now have TaskCompletionSource for efficiently awaiting a result.Gust
Thread.Sleep doesn't even work as specified, so it should never, ever be used. Sleep(16) has an average time of 30ms - that's just completely broken. The function needs to be deleted from the API.Backward
Z
19

I would like to answer this question from a coding-politics perspective, which may or may not be helpful to anyone. But particularly when you're dealing with tools that are intended for 9-5 corporate programmers, people who write documentation tend to use words like "should not" and "never" to mean "don't do this unless you really know what you're doing and why".

A couple of my other favorites in the C# world are that they tell you to "never call lock(this)" or "never call GC.Collect()". These two are forcefully declared in many blogs and official documentation, and IMO are complete misinformation. On some level this misinformation serves its purpose, in that it keeps the beginners away from doing things they don't understand before fully researching the alternatives, but at the same time, it makes it difficult to find REAL information via search-engines that all seem to point to articles telling you not to do something while offering no answer to the question "why not?"

Politically, it boils down to what people consider "good design" or "bad design". Official documentation should not be dictating the design of my application. If there's truly a technical reason that you shouldn't call sleep(), then IMO the documentation should state that it is totally okay to call it under specific scenarios, but maybe offer some alternative solutions that are scenario independent or more appropriate for the other scenarios.

Clearly calling "sleep()" is useful in many situations when deadlines are clearly defined in real-world-time terms, however, there are more sophisticated systems for waiting on and signalling threads that should be considered and understood before you start throwing sleep() into your code, and throwing unnecessary sleep() statements in your code is generally considered a beginners' tactic.

Zaid answered 11/8, 2016 at 18:41 Comment(0)
B
6

It is the 1).spinning and 2).polling loop of your examples that people caution against, not the Thread.Sleep() part. I think Thread.Sleep() is usually added to easily improve code that is spinning or in a polling loop, so it is just associated with "bad" code.

In addition people do stuff like:

while(inWait)Thread.Sleep(5000); 

where the variable inWait is not accessed in a thread-safe manner, which also causes problems.

What programmers want to see is the threads controlled by Events and Signaling and Locking constructs, and when you do that you won't have need for Thread.Sleep(), and the concerns about thread-safe variable access are also eliminated. As an example, could you create an event handler associated with the FileSystemWatcher class and use an event to trigger your 2nd example instead of looping?

As Andreas N. mentioned, read Threading in C#, by Joe Albahari, it is really really good.

Beetroot answered 20/3, 2015 at 19:32 Comment(5)
So what is the alternative to doing what's in your code example?Barometer
kuhaku - Yes, good question. Obviously Thread.Sleep() is a shortcut, and sometimes a big shortcut. For the example above, the rest of the code in the function below the polling loop "while(inWait)Thread.Sleep(5000); " is a new function. That new function is a delegate (callback function) and you pass it to whatever is setting the "inWait" flag, and instead of changing the "inWait" flag, the callback is invoked. This was the shortest example I could find: myelin.co.nz/notes/callbacks/cs-delegates.htmlBeetroot
Just to make sure I got it, you mean to wrap Thread.Sleep() with another function, and call that in the while loop?Barometer
Is that article you linked still relevant? It was last updated 10 years ago.Elodia
@Mike Thread.Sleep doesn't work as specified - that's the big problem - You ask it to suspend the thread for X ms and you won't see the thread wake up until 2*X ms has passed. That's just horrible, unusable behavior. It has nothing to do with people accessing inWait from another thread - that's a pretty specific caveat.Backward
C
6

Sleep is used in cases where independent program(s) that you have no control over may sometimes use a commonly used resource (say, a file), that your program needs to access when it runs, and when the resource is in use by these other programs your program is blocked from using it. In this case, where you access the resource in your code, you put your access of the resource in a try-catch (to catch the exception when you can't access the resource), and you put this in a while loop. If the resource is free, the sleep never gets called. But if the resource is blocked, then you sleep for an appropriate amount of time, and attempt to access the resource again (this why you're looping). However, bear in mind that you must put some kind of limiter on the loop, so it's not a potentially infinite loop. You can set your limiting condition to be N number of attempts (this is what I usually use), or check the system clock, add a fixed amount of time to get a time limit, and quit attempting access if you hit the time limit.

Conte answered 22/7, 2015 at 15:5 Comment(0)
D
5

I have a use case that I don't quite see covered here, and will argue that this is a valid reason to use Thread.Sleep():

In a console application running cleanup jobs, I need to make a large amount of fairly expensive database calls, to a DB shared by thousands of concurrent users. In order to not hammer the DB and exclude others for hours, I'll need a pause between calls, in the order of 100 ms. This is not related to timing, just to yielding access to the DB for other threads.

Spending 2000-8000 cycles on context switching between calls that may take 500 ms to execute is benign, as does having 1 MB of stack for the thread, which runs as a single instance on a server.

Desist answered 12/5, 2020 at 11:0 Comment(1)
Yeap, using Thread.Sleep in a single-thread, single-purpose Console application like the one you describe is perfectly OK.Feinberg
M
0

I think using timers is a good alternative. If you want a certain action to be performed once every x milliseconds if you you use Thread.sleep(x) your action will be performed every x+y seconds, where y is the time needed for your action to be performed.

Meteorology answered 6/2, 2022 at 15:33 Comment(0)
W
-8

For those of you who hasn't seen one valid argument against use of Thread.Sleep in SCENARIO 2, there really is one - application exit be held up by the while loop (SCENARIO 1/3 is just plain stupid so not worthy of more mentioning)

Many who pretend to be in-the-know, screaming Thread.Sleep is evil failed to mentioned a single valid reason for those of us who demanded a practical reason not to use it - but here it is, thanks to Pete - Thread.Sleep is Evil (can be easily avoided with a timer/handler)

    static void Main(string[] args)
    {
        Thread t = new Thread(new ThreadStart(ThreadFunc));
        t.Start();

        Console.WriteLine("Hit any key to exit.");
        Console.ReadLine();

        Console.WriteLine("App exiting");
        return;
    }

    static void ThreadFunc()
    {
        int i=0;
        try
        {
            while (true)
            {
                Console.WriteLine(Thread.CurrentThread.ThreadState.ToString() + " " + i);

                Thread.Sleep(1000 * 10);
                i++;
            }
        }
        finally
        {
            Console.WriteLine("Exiting while loop");
        }
        return;
    }
Wingding answered 20/2, 2013 at 1:40 Comment(2)
-, nope, Thread.Sleep is not the reason for this (the new thread an the continous while-loop is)! you may just remove the Thread.Sleep-line - et voila: the program won't exit as well...Planetstruck
In this case, it would be valid to use Auto/ManualResetEvent and use it instead of Thread.Sleep. Then in a Timer, set auto/manualresetevent so the loop can continue.Yacano
H
-8

I agree with many here, but I also think it depends.

Recently I did this code:

private void animate(FlowLayoutPanel element, int start, int end)
{
    bool asc = end > start;
    element.Show();
    while (start != end) {
        start += asc ? 1 : -1;
        element.Height = start;
        Thread.Sleep(1);
    }
    if (!asc)
    {
        element.Hide();
    }
    element.Focus();
}

It was a simple animate-function, and I used Thread.Sleep on it.

My conclusion, if it does the job, use it.

Hellebore answered 1/9, 2015 at 14:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.