views:

1950

answers:

2

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
}
+2  A: 

SpinWait is design to wait without yielding the current timeslice

It is designed for situations where you know you'll want to do something in a very short time so losing you timeslice will be excessive.

I was under the impression Thread.Yield(x) for any value of x < the thread quantum was equivalent, including zero though I have no benchmarks to that effect.

ShuggyCoUk
+4  A: 

SpinWait is useful on hyperthreaded processors. With hyperthreading, multiple OS scheduled threads can be running on the same physical processor, sharing the processor resources. SpinWait indicates to the processor that you are not doing any useful work and that it should run code from a different logical CPU. As the name suggests, it is typically used when you are spinning.

Suppose you have code like:

while (!foo) {} // Spin until foo is set.

If this thread is running on a thread on a hyperthreaded processor, it is consuming processor resources that could be used for other threads running on the processor.

By changing to:

while (!foo) {Thread.SpinWait(1);}

We are indicating to the CPU to give some resources to the other thread.

SpinWait does not affect OS scheduling of threads.

For your main questions about the "Ultimate Yield", it depends heavily on your situation - you won't be able to get a good answer without clarifying why you want a thread to yield. From my perspective, the best way to yield the processor is getting the thread to enter a wait state and only waking when there is work to do. Anything else is just wasting CPU time.

Michael
How is a hyperthreaded processor different from dual core processors? Btw, I implemented the "Ultimate Yield" to hide the implementation details, I obviously still have to consider which yield make the most sense a certain scenario.
Hermann
In dual core processors, each core has its own state and execution resources. Most of the time each core can be viewed as its own distinct CPU, they just share a package. A hyperthreaded CPU has duplicated state, but not execution resources. It appears to the OS as multiple CPU's. There are only finite execute resources on the processor and it switches between the different threads which have been scheduled on it. Hyperthreading and multicore do exist - you can buy a quad core, hyperthreaded Core i7 prcoessor. 4 cores, 8 runnable threads.
Michael
Or just read http://en.wikipedia.org/wiki/Hyper-threading.
Michael
Yeah, I just did. I was more interested in how Thread.SpinWait could be used in my method than what HyperThreading actually means. For example, would it make sense to add "ThreadYieldTarget.AnyThreadOnHyperThreadedProcessor" and call Thread.SpinWait(1)?
Hermann
No that would not make sense. SpinWait is primarily used for implementing spinlocks. You shouldn't think of it as a yield.
Michael
If you don't know what SpinWait does (in detail) you shouldn't think about using it...
ShuggyCoUk