Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Thread.Sleep(1) special?

Joe Duffy (author of Concurrent Programming on Windows) writes in this blog article that Thread.Sleep(1) is preferred over Thread.Sleep(0) because it will suspend for same and lower priority threads, not just equal priority threads as for Thread.Sleep(0).

The .NET version of MSDN says that Thread.Sleep(0) is special, it will suspend this thread and allow other waiting threads to execute. But it says nothing about Thread.Sleep(1) (for any .NET version).

So, is Thread.Sleep(1) actually doing anything special?

Background:

I'm refreshing my knowledge of concurrent programming. I wrote some C# code to visibly show that pre/post increments and decrements are non-atomic and therefore not thread-safe.

To avoid needing to create hundreds of threads I place a Thread.Sleep(0) after incrementing a shared variable to force the scheduler to run another thread. This regular swapping of threads makes the non-atomic nature of pre/post increment/decrement more obvious.

Thread.Sleep(0) appears to causes no additional delay, as expected. However if I change this to Thread.Sleep(1), it appears to revert to normal sleep behaviour (eg. I get roughly a minimum of 1ms delay).

This would mean that while Thread.Sleep(1) may be preferred, any code that uses it in a loop would run much slower.

This SO question "Could someone explain this interesting behaviour with Sleep(1)?" is sort of relevant, but it is C++ focused and just repeats the guidance in Joe Duffy's blog article.

Here's my code for anyone interested (copied from LinqPad, so you may need to add a class around it):

int x = 0;  void Main() {     List<Thread> threadList=new List<Thread>();     Stopwatch sw=new Stopwatch();      for(int i=0; i<20; i++)     {         threadList.Add(new Thread(Go));          threadList[i].Priority=ThreadPriority.Lowest;     }      sw.Start();      foreach (Thread thread in threadList)     {         thread.Start();     }        foreach (Thread thread in threadList)     {         thread.Join();     }       sw.Stop();     Console.WriteLine(sw.ElapsedMilliseconds);      Thread.Sleep(200);     Console.WriteLine(x); }  void Go() {     for(int i=0;i<10000;i++)     {         x++;         Thread.Sleep(0);     } } 
like image 748
Ash Avatar asked May 16 '13 09:05

Ash


People also ask

Why thread sleep is not recommended?

If given a wait of 5000 Milliseconds(5 seconds) and an element just take just 1-2 seconds to load, script will still wait for another 3 seconds which is bad as it is unnecessarily increasing the execution time. So thread. sleep() increases the execution time in cases where elements are loaded in no due time.

Is it a good practice to use thread sleep?

The reason people discourage Thread. sleep is because it's frequently used in an ill attempt to fix a race condition, used where notification based synchronization is a much better choice etc.

How long is thread sleep 1000?

For example, with thread. sleep(1000), you intended 1,000 milliseconds, but it could potentially sleep for more than 1,000 milliseconds too as it waits for its turn in the scheduler. Each thread has its own use of CPU and virtual memory.

How long should thread sleep?

If it is a UI worker thread, as long as they have some kind of progress indicator, anywhere up to half a second should be good enough. The UI should be responsive during the operation since its a background thread and you definitely have enough CPU time available to check every 500 ms.


1 Answers

You no longer need to use Sleep(1) instead of Sleep(0) because Microsoft changed the implementation of the Windows API Sleep().

From the MSDN documentation for Sleep(), this is what happens now with Sleep(0):

A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.

This is what used to happen in Windows XP:

A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution. This behavior changed starting with Windows Server 2003.

Note the difference between "any other thread" and "any other thread of equal priority".

The only reason that Joe Duffy suggests using Sleep(1) rather than Sleep(0) is because it is the shortest Sleep() value that will prevent the Sleep() from returning immediately if there are no other threads of equal priority ready to run, when running on Windows XP.

You don't need to worry about this for OS versions after Windows Server 2003 because of the change in behaviour of Sleep().

I draw your attention to this part of Joe's blog:

And even though there's an explicit Sleep in there, issuing it doesn't allow the producer to be scheduled because it's at a lower priority.

In XP, lower priority threads would be starved even if the main thread (of higher priority) did Sleep(0). Post-XP, this will no longer happen because Sleep(0) will allow the lower priority threads to run.

like image 54
Matthew Watson Avatar answered Sep 18 '22 17:09

Matthew Watson