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的资料请关注www.887551.com其它相关文章!