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

我试图编写最终的“收益率”方法来产生当前时间片到其他线程。 到目前为止,我发现有几种不同的方法可以使线程产生分配的时间片。 我只是想确保我正确解释他们,因为文件不是很清楚。 所以,从我读到的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], Thread.Sleep(1)通常会挂起线程大约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 } 

SpinWait在超线程处理器上很有用。 利用超线程,多个OS预定线程可以在同一个物理处理器上运行,共享处理器资源。 SpinWait向处理器表明您没有做任何有用的工作,并且应该从不同的逻辑CPU运行代码。 顾名思义,它通常用于纺纱。

假设你有这样的代码:

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

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

通过改变为:

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

我们正在向CPU表明给其他线程一些资源。

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

对于关于“终极收益”的主要问题,这很大程度上取决于您的情况 – 如果不说明为什么您希望线程产生收益,您将无法得到很好的答案。 从我的angular度来看,产生处理器的最好方法是使线程进入等待状态,只有在有工作要做时才会唤醒。 别的什么都只是浪费CPU时间。

Jeff Moser( http://www.moserware.com/2008/09/how-do-locks-lock.html )中的文章“Locks Lock是如何做的”可以让我们了解一下SpinWait的机制。 引用文件:

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

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

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

} FCIMPLEND

进一步的潜水显示“YieldProcessor”就是这个macros:

#define YieldProcessor()__asm {rep nop}

这是一个“重复禁止”汇编指令。 在英特尔指令集手册中也称为“暂停 – 自旋循环提示”。 这意味着CPU知道我们想要完成的旋转等待。

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

SpinWait是devise等待, 不会产生当前的时间片

它是专为那些你知道在短时间内想要做某些事情的情况而devise的,所以失去你的时间片将会过度。

我的印象Thread.Yield(x)的任何值x <线程的量子是等价的,包括零,但我没有这个效果的基准。

除了其他答案之外,这里还有一些分析数字。

(!)不要太认真对待这个分析! 只是为了说明上述答案的数字,大致比较价值的大小。

 static void Profile(Action func) { var sw = new Stopwatch(); var beginTime = DateTime.Now; ulong count = 0; while (DateTime.Now.Subtract(beginTime).TotalSeconds < 5) { sw.Start(); func(); sw.Stop(); count++; } Console.WriteLine($"Made {count} iterations in ~5s. Total sleep time {sw.ElapsedMilliseconds}[ms]. Mean time = {sw.ElapsedMilliseconds/(double) count} [ms]"); } Profile(()=>Thread.Sleep(0)); Profile(()=>Thread.Sleep(1)); Profile(()=>Thread.Yield()); Profile(()=>Thread.SpinWait(1)); 

纺纱循环约5秒的结果:

 Function | CPU % | Iters made | Total sleep | Invoke | | | time [ms] | time [ms] ===================================================================== Sleep(0) | 100 0 | 2318103 | 482 | 0.00020 Sleep(1) | 6 0 | 4586 | 5456 | 1.08971 Yield() | 100 0 | 2495220 | 364 | 0.00010 SpinWait(1)| 100 0 | 2668745 | 81 | 0.00003 

用Mono 4.2.3 x86_64制作