Date: | October 4, 2005 / year-entry #290 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20051004-09/?p=33923 |
Comments: | 20 |
Summary: | More often I see the reverse of the "Low priority threads can run even when higher priority threads are running" problem. Namely, people who think that Sleep(0) is a clean way to yield CPU. For example, they might have run out of things to do and merely wish to wait for another thread to produce... |
More often I see the reverse of the "Low priority threads can run even when higher priority threads are running" problem. Namely, people who think that Recall that the scheduler looks for the highest priority runnable thread, and if there is a tie, all the candidates share CPU roughly equally. A thread can call If there is a unique runnable thread with the highest priority, it can call Therefore, if you use The best thing to do is to wait on a proper synchronization object so that your thread goes to sleep until there is work to do. If you can't do that for some reason, at least sleep for a nonzero amount of time. That way, for that brief moment, your thread is not runnable and other threads—including lower-priority threads—get a chance to run. (This will also reduce power consumption somewhat, though not as much as waiting on a proper synchronization object.) |
Comments (20)
Comments are closed. |
Interesting fact to know, but I can’t imagine why anybody would ever think Sleep(0) was a good idea in the first place.
It seems common sense one would read this as a pointless roadblock that just eats cpu because we’re executing a step that doesn’t actually do anything, so no sleep’ing is going on. A microscopic step, but still a step.
I know I’ve done this… as a pure yeild, in a reader-writer lock for a low contention scenarios (readers:writers ~ 1000000:1) so wanted the optimal reader entry (no writer) being no more than an interloced operation. But this meant there was some signigificant cost on a writer entry (spin on InterlockedCompareExchange/Sleep(0)).
TANSTAAFL, if used where there were a lot of writers, you would see the cost.
What about SwitchToThread(), is that any different (or better) than Sleep(0)?
Cool! I must confess to being one of those who has always believed that calling sleep(0) was a good way to tell the OS "Well, I’m done – you can allocate my remaining CPU time to someone else".
sigh.
Will Sleep(1) work? Won’t that it give a whole quantom to a different thread?
Invariably, I end up using sleep instead of a "proper synchronization object" when work with files. For instance, a log file used by multiple processes. I’ve always wanted some form of a "file changed" notification and "file is no longer locked" notification.
Is there a way to do this without polling?
A while back I had to modify a piece of code that started two threads (A and B). The first thread (A) would do some initialization work and then lower it’s priority under tha assumption that the second thread (B) of normal priority would come in immediately thereafter and finish up the initialization. In some cases though, it seemed like thread A kept running for a short while before thread B would come in and finish up the initialization. It’s almost like the call to SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL) did not trigger the scheduler to reschedule the threads immediately. Note that this scenario was exhibited on a single processor machine.
Do you have any insight on specifically when the scheduler reschedules threads? Maybe this is where a Sleep(0) might have helped?
In the end, I added some specific synchronization code in to fix the problem and force the proper order of operations.
John,
You mean besides the filesystem change notification API’s?
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/obtaining_directory_change_notifications.asp
The File Change Notification APIs don’t help here. You pretty much have to just do back off. That said, you would never "back off" using a CPU-bound spin, so Sleep(0) and the quantum-ending effects would not be noticable.
There’s no good solution for this one. Asynchronous/cancellable file creation/opening might help but even at the NT layer I don’t think that there’s a way to block on waiting for handles with incompatible sharing flags to be closed.
The File Change Notification APIs help a lot here – in fact, they’re exactly what is required. You ask for an object to be signalled when the file changes, and wait for the object to be signalled. This allows any other threads/processes to run and modify the file, and then as soon as your thread is eligable to run, it will stop waiting. There’s no screwing around with sleeping or "backing off" – you just wait without using up CPU time.
To quote from the docs referenecd just above: "This function also returns a handle that can be waited on by using the wait functions" – which includes our friend WaitForMultipleObjectsEx.
If you don’t know and love WaitForMultipleObjectsEx and its friends in detail you’re never going to be able to write high quality threaded applications on Windows, and you’ll have trouble writing software that isn’t a CPU hog causing problems for every other application on the system.
I think Sleep(0) is a holdover from cooperative multi-tasking days (Windows 3.x), where the scheduler couldn’t pre-empt your thread, so you had to perform some specific yielding operation. Sleep(0) sure looks like one…
When trying to do realtime stuff this is most annoying… Sleep(0) will yield to higher or equal priority threads, Sleep(1) will go away for up to 15ms, which sucks.
File Change Notification APIs only help if the other file user is guaranteed to change the file before releasing it. If it ever releases the file unchanged you’re back to polling. But hopefully on a sleep() longer than 0.
"When trying to do realtime stuff this is most annoying… Sleep(0) will yield to higher or equal priority threads, Sleep(1) will go away for up to 15ms, which sucks."
Agree 100%. This problem shows up in game development all the time.
"It’s almost like the call to SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL) did not trigger the scheduler to reschedule the threads immediately."
That’s exactly what I found, too. A Sleep(0) after the SetThreadPriority call seemed to help.
I the past I have used sleep(0) to mean “This is a good time to switch threads” E.g. by giving up some of my CPU time at the top of the loop I am less likely to be switched out while I have a lot of locks etc in the middle of the loop
And then there’s YieldProcessor(), which (I read in MSDN) you need when doing spin-locks on hyperthreaded systems.
PingBack from http://blogs.ijw.co.nz/chris/index.php/2009/04/theres-a-third-hard-thing-about-concurrency/