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制作