SwitchToThread/Thread.Yield vs. Thread.Sleep(0) vs. Thead.Sleep(1)

Viewed 14044

I am trying to write the ultimate "Yield" method to yield the current time slice to other threads. So far I have found that there are several different ways to make the thread yield its allocated time slice. I just want to make sure I am interpreting them correctly since the documentation is not very clear. So, from what I have read on stackoverflow, MSDN and various blog posts, the following options exist that all have different advantages / disadvantages:

SwitchToThread [win32] / Thread.Yield [.NET 4 Beta 1]: yields to any thread on same processor

  • Advantage: about twice as fast as Thread.Sleep(0)
  • Disadvantage: yields only to threads on same processor

Thread.Sleep(0): yields to any thread of same or higher priority on any processor

  • Advantage: faster than Thread.Sleep(1)
  • Disadvantage: yields only to threads of same or higher priority

Thread.Sleep(1): yields to any thread on any processor

  • Advantage: yields to any thread on any processor
  • Disadvantage: slowest option (Thread.Sleep(1) will usually suspend the thread by about 15ms if timeBeginPeriod/timeEndPeriod [win32] are not used)

What about Thread.SpinWait? Can that be used for yielding the time slice of the thread? If not, what is it used for?

I there something else I have missed or incorrectly interpreted. I'd be grateful if you could correct / add to my understanding.

This is how my Yield method looks like so far:

public static class Thread
{
    [DllImport("kernel32.dll")]
    static extern bool SwitchToThread();

    [DllImport("winmm.dll")]
    internal static extern uint timeBeginPeriod(uint period);

    [DllImport("winmm.dll")]
    internal static extern uint timeEndPeriod(uint period);

    /// <summary>  yields time slice of current thread to specified target threads </summary>
    public static void YieldTo(ThreadYieldTarget threadYieldTarget)
    {
        switch (threadYieldTarget) {
            case ThreadYieldTarget.None: 
                break; 
            case ThreadYieldTarget.AnyThreadOnAnyProcessor:
                timeBeginPeriod(1); //reduce sleep to actually 1ms instead of system time slice with is around 15ms
                System.Threading.Thread.Sleep(1); 
                timeEndPeriod(1); //undo
                break;
            case ThreadYieldTarget.SameOrHigherPriorityThreadOnAnyProcessor:
                System.Threading.Thread.Sleep(0); 
                break;
            case ThreadYieldTarget.AnyThreadOnSameProcessor:
                SwitchToThread();
                break;
            default: throw new ArgumentOutOfRangeException("threadYieldTarget");
        }
    }
}

public enum ThreadYieldTarget
{
    /// <summary>  Operation system will decide when to interrupt the thread </summary>
    None,
    /// <summary>  Yield time slice to any other thread on any processor </summary>
    AnyThreadOnAnyProcessor,
    /// <summary>  Yield time slice to other thread of same or higher piority on any processor </summary>
    SameOrHigherPriorityThreadOnAnyProcessor,
    /// <summary> Yield time slice to any other thread on same processor </summary>
    AnyThreadOnSameProcessor
}
4 Answers

The article "How does Locks Lock" by Jeff Moser (http://www.moserware.com/2008/09/how-do-locks-lock.html) can give some inside about the mechanics of SpinWait. To cite the document:

What exactly is it doing? Looking at Rotor's clr/src/vm/comsynchronizable.cpp gives us the reality:

FCIMPL1(void, ThreadNative::SpinWait, int iterations) { WRAPPER_CONTRACT; STATIC_CONTRACT_SO_TOLERANT;

for(int i = 0; i < iterations; i++)
    YieldProcessor();

} FCIMPLEND

Further diving shows that "YieldProcessor" is this macro:

#define YieldProcessor() __asm { rep nop }

This is a "repeat no-op" assembly instruction. It's also known in the Intel instruction set manual as "PAUSE - Spin Loop Hint." This means that the CPU knows about the spin waiting that we're wanting to accomplish.

Related: http://msdn.microsoft.com/en-us/library/ms687419(VS.85).aspx http://www.moserware.com/2008/09/how-do-locks-lock.html#lockfn7

Related