61

From the MSDN is not really clear its purpose.

Can it be used to simulate an intensive CPU calculation test?

Drake
  • 8,225
  • 15
  • 71
  • 104

5 Answers5

112

It's used as a replacement for very short-term sleep calls.

When you do multi-threaded locking, if the resource you're attempting to acquire is already locked, you typically go to sleep and wait for it to become free. When you do this, you give up the rest of the time that you were allocated by the scheduler to use the processor so someone else can have a go. Normally this is fine, especially for long waits, like waiting for IO, loads of other processes can run on the CPU while you're waiting for the disk spindle to rotate.

However, sometimes, you're waiting for a tiny amount of time. In these cases, you would normally give up your remaining time anyway and wait for all the other threads to do their thing before getting another go.. so you can cheat, instead of waiting, you sit there continually polling in a 'are we nearly there yet?' way. If the lock is only held for a fraction of your remaining time, this becomes a very effective means of waiting, its also very efficient as the scheduler doesn't have to get involved in rearranging all the other threads to use the time you give up if you waited normally.

Obviously, if you spin every time you want a lock, you're not going to be very popular, your app will become sluggish and use 100% CPU, but in very small doses, at the right time, it makes the app more responsive.

If you're now thinking 'when should I use it?', that's a tricky call to make - if you have a resource that is very often locked and unlocked very quickly, then a spinlock around that instead of a wait is a good idea (and then test your app for performance), if you try spinning for a short time, and then fall back to a normal wait, that's a reasonable way too. But generally, you will never need to use it.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
  • Say we want to achieve a 50ms delay on average, how many iterations do we specify? – Charles Okwuagwu May 20 '13 at 09:19
  • 3
    `SpinWait` will rarely cause you to get stuck at 100% CPU utilization. This is because it starts causing the thread to yield or sleep if it spins for too long. Specifically, if the number of spins is greater than or equal to 10, it will `sleep(1)` for every 19th spin and `sleep(0)` for every 4th spin, and `yield` if and only if the next spin is determined to have yielding potential. It can `sleep` or `yield` on multi-processor systems at any point and doesn't need to wait for the initial 10 spins. On a single processor system, it will only `sleep` or `yield` if the spin count 10 or below. – Michael J. Gray Mar 25 '14 at 17:20
  • 4
    @MichaelJ.Gray: You're confusing `Thread.SpinWait()` (the question and answer) with `var sw = new SpinWait(); sw.SpinOnce();` which does the internal spin count till 10 ... – DeepSpace101 Jun 25 '15 at 02:24
  • 3
    @CharlesOkwuagwu for tens of milliseconds, you'd probably be better off with a simple thread.sleep. – Stephen Kennedy Oct 09 '18 at 14:27
  • @StephenKennedy "for tens of milliseconds", you would absolutely always (not probably) use a simple thread.sleep. Modern day processors can execute millions of instructions in tens of milliseconds on a single core. – John Pankowicz Jan 26 '21 at 14:10
  • @JohnPankowicz splitting hairs here but ... 'probably' because it depends on how much you care about resolution. E.g. if you want to wake up _very_ close to 50ms, you'll need to thread.sleep somewhat less than that, check the time and spinwait for the remaining interval. – Stephen Kennedy Jan 27 '21 at 17:28
  • @StephenKennedy I was originally concerned with hanging other apps during the spinlock. But I learned that most modern OS's use preemptive scheduling, so this should not be an issue. – John Pankowicz Jan 28 '21 at 14:21
78

The purpose is to do a "cheap" wait if you believe that the condition you're waiting for will come true very, very soon. Normally if you're waiting for something, you let the thread go to sleep and the processor/OS will context switch to another thread. Context switches aren't particularly cheap, so if you have advanced knowledge of the situation and believe it's cheaper to wait than to context switch, you spin wait.

My advice: if you need to ask, you don't need to use it. (I've never wanted it myself.) Basically it's one of those things which is really useful in a very few situations, but most people should leave well alone.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    I guess this one goes with the whole category of "unless you have measured and found there to be a problem..." – jerryjvl Jul 07 '09 at 11:28
  • @jon skeet So by default you would recommend simply using lock(obj) and only use spinwait if you've determined via benchmarking that it is faster for your scenario? – rollsch Apr 06 '21 at 03:47
  • @rolls: Yes, absolutely. It's a very niche feature. – Jon Skeet Apr 06 '21 at 05:38
16

As a side note, Microsoft have gotten rid of the thread dispatcher spinlock mechanism from Windows 7, since it didn't scale well for multicore CPU's. Have a look at this:

EDIT: As Channel9 is no more (stupid decision in my view), the link I posted no longer works. Instead you can find the video on YouTube

Jason Evans
  • 28,906
  • 14
  • 90
  • 154
6

As far as I'm concerned (and I'm happy for corrections!), the only use of spin waits is when implementing a locking or inter-thread-callback mechanism. And neither should be done manually (usually), since they already exist.

When you've locked a resource and another thread requests synchronized access to it, it basically has to wait for the first thread to finish using it. This waiting can be done by simply spinning in a loop (or otherwise sleeping + context switching as mentioned by Jon).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    One exception - inside the `Render()` cycle of a game engine to cap frame rate. None of the other `Sleep()` mechanisms provide a good enough guarantee about when you'll be switched back in. – Basic Oct 18 '13 at 09:21
  • 2
    @Basic True although what’s often done is that you skip laps in the render cycle rather than having an explicit (nested) wait loop. The effect is the same of course. – Konrad Rudolph Oct 19 '13 at 11:20
0

If you need a precise sleep time you cannot use Thread.Sleep() because it is very unprecise (± 25 ms depending on CPU load) because Windows is a multitasking operating system. I use the following code for timing relevant and short sleep intervals of a few milliseconds only.

// IMPORTANT:
// Use this only for very short sleep intervals. 
// One CPU core will run with 100% load during this interval.
public static void SleepPrecise(int s32_Interval) // in ms
{
    if (s32_Interval <= 0)
        return;

    // System.Diagnostics.Stopwatch uses the performance counter in the processor 
    // which has a precision of micro seconds or even nano seconds.
    Stopwatch i_Watch = new Stopwatch();
    i_Watch.Start();

    while (i_Watch.ElapsedMilliseconds < s32_Interval)
    {
        // SpinWait(80000) --> 1 ms on a 3.6 GHz AMD Radeon processor
        Thread.SpinWait(5000);
    }
}

If you need extremely high timing precision you can additionally:

Thread.CurrentThread.Priority = ThreadPriority.Highest;
try
{
   .... execute your timing relevant code
}
finally
{
    Thread.CurrentThread.Priority = ThreadPriority.Normal;
}
Elmue
  • 7,602
  • 3
  • 47
  • 57