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

38 .net concurrency multithreading

我正在尝试编写最终的"Yield"方法,以将当前时间片提供给其他线程.到目前为止,我发现有几种不同的方法可以使线程产生分配的时间片.我只是想确保我正确地解释它们,因为文档不是很清楚.因此,从我在stackoverflow,MSDN和各种博客文章中看到的内容,存在以下选项,它们都有不同的优点/缺点:

SwitchToThread[win32]/Thread.Yield[.NET 4 Beta 1]:产生同一处理器上的任何线程

  • 优势:大约快两倍 Thread.Sleep(0)
  • 缺点:仅产生同一处理器上的线程

Thread.Sleep(0):在任何处理器上产生任何具有相同或更高优先级的线程

  • 优点:比快 Thread.Sleep(1)
  • 缺点:仅产生相同或更高优先级的线程

Thread.Sleep(1):屈服于任何处理器上的任何线程

  • 优点:产生任何处理器上的任何线程
  • 缺点:最慢选项(Thread.Sleep(1)如果 不使用timeBeginPeriod/ timeEndPeriod[win32],通常会将线程暂停约15ms )

怎么样Thread.SpinWait?可以用它来产生线程的时间片吗?如果没有,它用于什么?

我还有其他一些我错过或错误解释的东西.如果你能纠正/增加我的理解,我将不胜感激.

这就是我的Yield方法到目前为止的样子:

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
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*ael 12

SpinWait在超线程处理器上很有用.通过超线程,多个OS调度线程可以在同一物理处理器上运行,共享处理器资源.SpinWait向处理器指示您没有做任何有用的工作,并且它应该从不同的逻辑CPU运行代码.顾名思义,它通常在您旋转时使用.

假设您有以下代码:

while (!foo) {} // Spin until foo is set.
Run Code Online (Sandbox Code Playgroud)

如果此线程在超线程处理器上的线程上运行,则会消耗可用于处理器上运行的其他线程的处理器资源.

通过更改为:

while (!foo) {Thread.SpinWait(1);} 
Run Code Online (Sandbox Code Playgroud)

我们指示CPU为其他线程提供一些资源.

SpinWait不会影响线程的OS调度.

对于关于"终极收益"的主要问题,它在很大程度上取决于您的情况 - 如果没有说明您希望线程产生的原因,您将无法得到一个好的答案.从我的角度来看,产生处理器的最好方法是让线程进入等待状态,只有在有工作要做时才会醒来.其他任何东西只是在浪费CPU时间.

  • 如果您不知道SpinWait的作用(详细信息),您不应该考虑使用它... (2认同)

Shu*_*oUk 5

SpinWait 被设计为等待而不产生当前时间片

它是为这样的情况而设计的:您知道自己想要在很短的时间内完成某件事,因此失去时间片将是过多的。

我的印象是 Thread.Yield(x) 对于 x < 线程量子的任何值都是等效的,包括零,尽管我没有这方面的基准。


The*_*aot 5

Jeff Moser撰写的文章" Hows Locks Lock"(http://www.moserware.com/2008/09/how-do-locks-lock.html)可以给出一些关于SpinWait机制的内容.引用该文件:

到底是做什么的?看看Rotor的clr/src/vm/comsynchronizable.cpp给了我们现实:

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

for(int i = 0; i < iterations; i++)
    YieldProcessor();
Run Code Online (Sandbox Code Playgroud)

} FCIMPLEND

进一步的潜水表明"YieldProcessor"是这个宏:

#define YieldProcessor()__ asm {rep nop}

这是一个"重复无操作"汇编指令.它在英特尔指令集手册中也称为"PAUSE - Spin Loop Hint".这意味着CPU知道我们想要完成的旋转等待.

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