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结构中的代码:

  1. /// <summary>
  2. /// 循环一次
  3. /// </summary>
  4. /// <remarks>
  5. /// This is typically called in a loop, and may change in behavior based on the number of times a
  6. /// <see cref="SpinOnce"/> has been called thus far on this instance.
  7. /// </remarks>
  8. public void SpinOnce()
  9. {
  10. if (NextSpinWillYield)
  11. {
  12. int yieldsSoFar = (m_count >= YIELD_THRESHOLD ? m_count - YIELD_THRESHOLD : m_count);
  13. //③循环到20次时,执行Thread.Sleep(01)
  14. if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1))
  15. {
  16. //当前线程挂起,让出cpu
  17. //所有挂起的线程都有机会竞争当前时间片段,不限制线程优先级
  18. Thread.Sleep(1);
  19. }
  20. //②执行Thread.Yield()5次后,执行Thread.Sleep(0)
  21. else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1))
  22. {
  23. //当前线程挂起,让出cpu
  24. //(只允许那些优先级相等或更高的线程使用当前的CPU。
  25. //如果没有,那当前线程会重新使用CPU时间片)
  26. //(上面已说明,后续补充实现)
  27. Thread.Sleep(0);
  28. }
  29. else
  30. {
  31. //当前线程挂起(执行状态->就绪状态), 让出cpu,
  32. //(后续补充实现逻辑)
  33. Thread.Yield();
  34. }
  35. }
  36. else
  37. {
  38. //线程等待
  39. //4,8,16,32,64...位运算,2的n次方
  40. //①循环10次
  41. Thread.SpinWait(4 << m_count);
  42. }
  43. // m_count 递增; m_count 达到最大值后回滚Count =10
  44. m_count = (m_count == int.MaxValue ? YIELD_THRESHOLD : m_count + 1);
  45. }

 

  1. /// <summary>
  2. /// 重置循环计数器
  3. /// </summary>
  4. public void Reset()
  5. {
  6. m_count = 0;
  7. }
  8. #region Static Methods
  9. /// <summary>
  10. /// 循环.直到condition返回True
  11. /// </summary>
  12. public static void SpinUntil(Func<bool> condition)
  13. {
  14. SpinUntil(condition, Timeout.Infinite);
  15. }
  16. /// <summary>
  17. /// 循环,直到condition返回True或者时间达到timeout
  18. /// </summary>
  19. public static bool SpinUntil(Func<bool> condition, TimeSpan timeout)
  20. {
  21. //校验时间格式是否正确
  22. Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds;
  23. if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
  24. {
  25. throw new System.ArgumentOutOfRangeException(
  26. "timeout", timeout, "SpinWait_SpinUntil_TimeoutWrong");
  27. }
  28. return SpinUntil(condition, (int)timeout.TotalMilliseconds);
  29. }
  30. /// <summary>
  31. /// 直到condition返回True或者时间达到timeout.
  32. /// </summary>
  33. public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout)
  34. {
  35. //校验时间格式
  36. if (millisecondsTimeout < Timeout.Infinite)
  37. {
  38. throw new ArgumentOutOfRangeException(
  39. "millisecondsTimeout", millisecondsTimeout, "SpinWait_SpinUntil_TimeoutWrong");
  40. }
  41. //空值校验
  42. if (condition == null)
  43. {
  44. throw new ArgumentNullException("condition", "SpinWait_SpinUntil_ArgumentNull");
  45. }
  46. uint startTime = 0;
  47. if (millisecondsTimeout != 0 && millisecondsTimeout != Timeout.Infinite)
  48. {
  49. //自上次启动计算机以来所经过的时间(以毫秒为单位)。
  50. startTime = TimeoutHelper.GetTime();
  51. }
  52. SpinWait spinner = new SpinWait();
  53. while (!condition())
  54. {
  55. if (millisecondsTimeout == 0)
  56. {
  57. return false;
  58. }
  59. spinner.SpinOnce();
  60. //计时
  61. if (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield)
  62. {
  63. if (millisecondsTimeout <= (TimeoutHelper.GetTime() - startTime))
  64. {
  65. return false;
  66. }
  67. }
  68. }
  69. return true;
  70. }

  71. #endregion

 

 

 

版权声明:本文为xtt321原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xtt321/p/13864805.html