SpinWait封装常见旋转逻辑。在单处理器计算机上,始终使用 "生成" 而不是 "繁忙等待",在装有超线程技术的 Intel 处理器的计算机上,这有助于防止硬件线程不足。SpinWait 封装了一种很好的旋转和真正的生成。
SpinWait是一个值类型,这意味着低级别代码可以使用 SpinWait,而不必担心不必要的分配开销。SpinWait 对于普通应用程序通常不起作用。在大多数情况下,应使用由 .NET Framework 提供的同步类,如 Monitor 。但在需要自旋等待的大多数情况下, SpinWait 类型应优先于 Thread.SpinWait 方法。
System.Threading.SpinWait 是一种轻型同步类型,可用于低级方案,以避免执行内核事件所需的高成本上下文切换和内核转换。在多核计算机上,如果不得长时间保留资源,更高效的做法是,先让等待线程在用户模式下旋转几十或几百个周期,再重试获取资源。如果资源在旋转后可用,便节省了几千个周期。如果资源仍不可用,那么也只花了几个周期,仍可以进入基于内核的等待。这种“旋转后等待”的组合有时称为“两阶段等待操作” 。
SpinWait 旨在与包装内核事件(如 ManualResetEvent)的 .NET Framework 类型结合使用。SpinWait 本身也可以仅在一个程序中用于提供基本的旋转功能。
SpinWait 不仅仅只是空循环。谨慎实现后,它可以提供适用于一般情况的正确旋转行为,并且本身能够在旋转时间够长(大致是内核转换所需的时间长度)时自行启动上下文切换。例如,在单核计算机上,SpinWait 会立即生成线程的时间片,因为旋转会阻止所有线程取得进展。即使在多核计算机上,SpinWait 也会生成时间片,以防等待线程阻止优先级较高的线程或垃圾回收器。因此,若要在两阶段等待操作中使用 SpinWait,建议在 SpinWait 本身启动上下文切换前,先调用内核等待。SpinWait 提供每次调用 SpinOnce 前都可以检查的 NextSpinWillYield 属性。如果此属性返回 true,启动自己的等待操作。
看完官方说明一脸懵逼,将上面的语言用通俗的话来说,Thread.Sleep方法在执行时,会将阻止的时间的CPU切换至其他等待的进程,等到Thread.Sleep等待时间到后,再获取CPU的控制权继续执行下一步操作;SpinWait提供了While循环方法,在等待通过循环来阻止当前CPU的释放,一直等待当前方法执行完成然后释放。我们都知道进程在切换的时候会有时间与内存的消耗,所以尽可能使用SpinWait替代Thread.Sleep。
现在我们看下SpinWait结构中的代码:
/// <summary> /// 循环一次 /// </summary> /// <remarks> /// This is typically called in a loop, and may change in behavior based on the number of times a /// <see cref="SpinOnce"/> has been called thus far on this instance. /// </remarks> public void SpinOnce() { if (NextSpinWillYield) { int yieldsSoFar = (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) else if ((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 << m_count); } // m_count 递增; m_count 达到最大值后回滚Count =10 m_count = (m_count == int.MaxValue ? YIELD_THRESHOLD : m_count + 1); }
/// <summary> /// 重置循环计数器 /// </summary> public void Reset() { m_count = 0; } #region Static Methods /// <summary> /// 循环.直到condition返回True /// </summary> public static void SpinUntil(Func<bool> condition) { SpinUntil(condition, Timeout.Infinite); } /// <summary> /// 循环,直到condition返回True或者时间达到timeout /// </summary> public static bool SpinUntil(Func<bool> condition, TimeSpan timeout) { //校验时间格式是否正确 Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue) { throw new System.ArgumentOutOfRangeException( "timeout", timeout, "SpinWait_SpinUntil_TimeoutWrong"); } return SpinUntil(condition, (int)timeout.TotalMilliseconds); } /// <summary> /// 直到condition返回True或者时间达到timeout. /// </summary> public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout) { //校验时间格式 if (millisecondsTimeout < Timeout.Infinite) { throw new ArgumentOutOfRangeException( "millisecondsTimeout", millisecondsTimeout, "SpinWait_SpinUntil_TimeoutWrong"); } //空值校验 if (condition == null) { throw new ArgumentNullException("condition", "SpinWait_SpinUntil_ArgumentNull"); } uint startTime = 0; if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite) { //自上次启动计算机以来所经过的时间(以毫秒为单位)。 startTime = TimeoutHelper.GetTime(); } SpinWait spinner = new SpinWait(); while (!condition()) { if (millisecondsTimeout == 0) { return false; } spinner.SpinOnce(); //计时 if (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield) { if (millisecondsTimeout <= (TimeoutHelper.GetTime() - startTime)) { return false; } } } return true; } #endregion
以上就是详解c# SpinWait的详细内容,更多关于c# SpinWait的资料请关注小牛知识库其它相关文章!
本文向大家介绍详解C# FileStream类,包括了详解C# FileStream类的使用技巧和注意事项,需要的朋友参考一下 C# FileStream类 在 C# 语言中文件读写流使用 FileStream 类来表示,FileStream 类主要用于文件的读写,不仅能读写普通的文本文件,还可以读取图像文件、声音文件等不同格式的文件。区别于File类的是它对文件可进行分步读写,减小内存压力,缺点
本文向大家介绍c++常量详解,包括了c++常量详解的使用技巧和注意事项,需要的朋友参考一下 概念 常量是存放固定且不可变值的,一旦确定初始值则在程序其它地方不可改变, 所以const对象必须初始化。常量一般使用const关键字来修饰。 const 对象可以大致分为三类: 1. const int a const int a =10; int const b =10; 这两种格式是完全相同的。也就
本文向大家介绍详解 c# 克隆,包括了详解 c# 克隆的使用技巧和注意事项,需要的朋友参考一下 克隆方法是原型设计模式中必须使用的方式,它将返回一个与当前对象数据一致的对象。正如其名,犹如一个模子雕刻而出。克隆类型分为两种:浅克隆、深克隆。 1、浅克隆 浅克隆方式是最简单、最直接的方式。只需要类实现接口ICloneable(在命名空间System.Runtime.InteropServices下)
本文向大家介绍C++ Template应用详解,包括了C++ Template应用详解的使用技巧和注意事项,需要的朋友参考一下 引言 模板(Template)指C++程序设计设计语言中采用类型作为参数的程序设计,支持通用程序设计。C++ 的标准库提供许多有用的函数大多结合了模板的观念,如STL以及IO Stream。 函数模板 在c++入门中,很多人会接触swap(int&, int&)这样的函数
本文向大家介绍详解C# 反射(Reflection),包括了详解C# 反射(Reflection)的使用技巧和注意事项,需要的朋友参考一下 C# 反射(Reflection) 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。 您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中
本文向大家介绍详解C#之事件,包括了详解C#之事件的使用技巧和注意事项,需要的朋友参考一下 事件:定义了事件成员的类允许通知其他其他对象发生了特定的事情。具体的说,定义了事件成员的类能提供以下功能 1.方法能登记它对事件的关注 2.方法能注销它对事件的关注 3.事件发生时,登记了的方法将收到通知 类型之所以能提供事件通知功能,是因为类型维护了一个已登记方法的列表。事件发生后,类型将通知列表中所有已