详解c# SpinWait
SpinWait封装常见旋转逻辑。在单处理器计算机上,始终使用"生成"而不是"繁忙等待",在装有超线程技术的Intel处理器的计算机上,这有助于防止硬件线程不足。SpinWait封装了一种很好的旋转和真正的生成。
SpinWait是一个值类型,这意味着低级别代码可以使用SpinWait,而不必担心不必要的分配开销。SpinWait对于普通应用程序通常不起作用。在大多数情况下,应使用由.NETFramework提供的同步类,如Monitor。但在需要自旋等待的大多数情况下,SpinWait类型应优先于Thread.SpinWait方法。
System.Threading.SpinWait是一种轻型同步类型,可用于低级方案,以避免执行内核事件所需的高成本上下文切换和内核转换。在多核计算机上,如果不得长时间保留资源,更高效的做法是,先让等待线程在用户模式下旋转几十或几百个周期,再重试获取资源。如果资源在旋转后可用,便节省了几千个周期。如果资源仍不可用,那么也只花了几个周期,仍可以进入基于内核的等待。这种“旋转后等待”的组合有时称为“两阶段等待操作”。
SpinWait旨在与包装内核事件(如ManualResetEvent)的.NETFramework类型结合使用。SpinWait本身也可以仅在一个程序中用于提供基本的旋转功能。
SpinWait不仅仅只是空循环。谨慎实现后,它可以提供适用于一般情况的正确旋转行为,并且本身能够在旋转时间够长(大致是内核转换所需的时间长度)时自行启动上下文切换。例如,在单核计算机上,SpinWait会立即生成线程的时间片,因为旋转会阻止所有线程取得进展。即使在多核计算机上,SpinWait也会生成时间片,以防等待线程阻止优先级较高的线程或垃圾回收器。因此,若要在两阶段等待操作中使用SpinWait,建议在SpinWait本身启动上下文切换前,先调用内核等待。SpinWait提供每次调用SpinOnce前都可以检查的NextSpinWillYield属性。如果此属性返回true,启动自己的等待操作。
看完官方说明一脸懵逼,将上面的语言用通俗的话来说,Thread.Sleep方法在执行时,会将阻止的时间的CPU切换至其他等待的进程,等到Thread.Sleep等待时间到后,再获取CPU的控制权继续执行下一步操作;SpinWait提供了While循环方法,在等待通过循环来阻止当前CPU的释放,一直等待当前方法执行完成然后释放。我们都知道进程在切换的时候会有时间与内存的消耗,所以尽可能使用SpinWait替代Thread.Sleep。
现在我们看下SpinWait结构中的代码:
//////循环一次 /// //////Thisistypicallycalledinaloop,andmaychangeinbehaviorbasedonthenumberoftimesa /// publicvoidSpinOnce() { if(NextSpinWillYield) { intyieldsSoFar=(m_count>=YIELD_THRESHOLD?m_count-YIELD_THRESHOLD:m_count); //③循环到20次时,执行Thread.Sleep(01) if((yieldsSoFar%SLEEP_1_EVERY_HOW_MANY_TIMES)==(SLEEP_1_EVERY_HOW_MANY_TIMES-1)) { //当前线程挂起,让出cpu //所有挂起的线程都有机会竞争当前时间片段,不限制线程优先级 Thread.Sleep(1); } //②执行Thread.Yield()5次后,执行Thread.Sleep(0) elseif((yieldsSoFar%SLEEP_0_EVERY_HOW_MANY_TIMES)==(SLEEP_0_EVERY_HOW_MANY_TIMES-1)) { //当前线程挂起,让出cpu //(只允许那些优先级相等或更高的线程使用当前的CPU。 //如果没有,那当前线程会重新使用CPU时间片) //(上面已说明,后续补充实现) Thread.Sleep(0); } else { //当前线程挂起(执行状态->就绪状态),让出cpu, //(后续补充实现逻辑) Thread.Yield(); } } else { //线程等待 //4,8,16,32,64...位运算,2的n次方 //①循环10次 Thread.SpinWait(4<hasbeencalledthusfaronthisinstance. /// //////重置循环计数器 /// publicvoidReset() { m_count=0; } #regionStaticMethods //////循环.直到condition返回True /// publicstaticvoidSpinUntil(Funccondition) { SpinUntil(condition,Timeout.Infinite); } /// ///循环,直到condition返回True或者时间达到timeout /// publicstaticboolSpinUntil(Funccondition,TimeSpantimeout) { //校验时间格式是否正确 Int64totalMilliseconds=(Int64)timeout.TotalMilliseconds; if(totalMilliseconds<-1||totalMilliseconds>Int32.MaxValue) { thrownewSystem.ArgumentOutOfRangeException( "timeout",timeout,"SpinWait_SpinUntil_TimeoutWrong"); } returnSpinUntil(condition,(int)timeout.TotalMilliseconds); } /// ///直到condition返回True或者时间达到timeout. /// publicstaticboolSpinUntil(Funccondition,intmillisecondsTimeout) { //校验时间格式 if(millisecondsTimeout 以上就是详解c#SpinWait的详细内容,更多关于c#SpinWait的资料请关注毛票票其它相关文章!