【问题标题】:Is 16 milliseconds an unusually long length of time for an unblocked thread running on Windows to be waiting for execution?对于在 Windows 上运行的未阻塞线程等待执行,16 毫秒是否是一个异常长的时间?
【发布时间】:2012-01-09 12:07:00
【问题描述】:

最近,我使用 DSPACK 组件对我在 Delphi 6 中的 DirectShow 应用程序进行了一些深入的计时检查。作为诊断的一部分,我创建了一个关键部分类,它为大多数 Windows 编程语言中常见的关键部分对象添加了超时功能。如果第一次Acquire()和最后一次匹配Release()之间的持续时间超过X毫秒,则抛出异常。

最初我将超时设置为 10 毫秒。我在关键部分中包装的代码非常快,主要使用内存移动和填充来完成保护区域中包含的大多数操作。令我惊讶的是,我在看似随机的代码部分中出现了相当频繁的超时。有时它发生在迭代缓冲区列表并按顺序执行某些快速操作的代码块中,有时发生在仅在 Acquire() 和 Release() 调用之间清除标志的受保护代码的一小部分中。我注意到的唯一模式是超时发生时发现的持续时间以大约 16 毫秒的中值为中心。显然,在我上面提到的后一个例子中设置标志需要很长时间。

所以我的问题是:

1) Windows 线程管理代码是否有可能在相当频繁的基础上(大约每几秒一次)切换出一个未阻塞的线程并且在 16 毫秒或更长时间内不返回它?

2) 如果这是一个合理的情况,我可以采取哪些措施来减少这种情况发生,我应该考虑提升我的线程优先级吗?

3) 如果这不是一个合理的场景,我还应该查看或尝试什么作为分析技术来诊断真正的问题?

注意:我在具有 3 GB 内存的 Intel i5 Quad Core 上运行 Windows XP。此外,我需要在此代码中快速的原因是由于我在 DirectShow 过滤器图中选择的缓冲区大小(以毫秒为单位)。为了将延迟保持在最低限度,我的图表中的音频缓冲区每 50 毫秒交付一次。因此,任何占用该时间大部分时间的操作都是麻烦的。

【问题讨论】:

  • 你是如何测量时间的?
  • 我正在检查系统时钟。在 Delphi 中,这就是“现在”功能。
  • 这是一个开始的问题 - 这可能是系统时钟的粒度。
  • 我不是总是会超时而不是有时吗?或者错误的数量是否浮动?在任何一种情况下,我还能使用什么?由于 CPU 速度步进,性能计数器似乎是一个真正的蛇坑,那么还有什么?
  • 说实话,不确定。这很可能取决于 Windows 的版本。我很确定Sleep 的最小“有用”值为 15 毫秒。

标签: windows multithreading scheduled-tasks critical-section


【解决方案1】:

线程优先级决定何时运行就绪线程。但是,有一种饥饿预防机制。有一个所谓的平衡集管理器,它每秒唤醒一次,寻找大约 3 或 4 秒未运行的就绪线程,如果有,它会将其优先级提高到 15 并给它加倍正常量子。它一次(每秒)对不超过 10 个线程执行此操作,并且一次在每个优先级级别扫描不超过 16 个线程。在量程结束时,提升的优先级降至其基本值。您可以在 Windows Internals 书籍中找到更多信息。

因此,您观察到这是一种非常正常的行为,线程可能会在几秒钟内不运行。

您可能需要提升优先级或以其他方式考虑竞争 CPU 时间的其他线程。

【讨论】:

  • 另外,我想补充一点,随机内存块可能会被分页到磁盘,因此迭代缓冲区列表可能会导致页面错误并且线程等待从磁盘读取内存。
【解决方案2】:

除非您明确选择某些高精度计时器,否则就计时器分辨率而言,这听起来像是正常的 Windows 行为。 this msdn link中的一些细节

【讨论】:

  • 请参阅我对 Jon 的回复,了解如何测量时间。我想要你的 cmets。
【解决方案3】:

首先,我不确定 Delphi 的 Now 是否是毫秒精度测量的好选择。 GetTickCountQueryPerformanceCoutner API 会是更好的选择。

当临界区锁定没有冲突时,一切都运行得非常快,但是如果您尝试进入当前锁定在另一个线程上的临界区,最终您会在内部内核对象(互斥锁或事件)上遇到等待操作),这涉及让出对线程的控制并等待调度程序稍后将控制权交还。

上面的“稍后”取决于一些事情,包括上面提到的优先级,并且您在测试中忽略了一件重要的事情 - 测试时的总体 CPU 负载是多少。负载越多,让线程很快继续执行的机会就越小。 16 毫秒的时间看起来可能还在合理的容忍范围内,总而言之,这可能取决于您的实际实现。

【讨论】:

  • 我阅读的网络文档表明,可靠地检测 CPU 速度变化,这将使 QueryPerformanceCounter() 测量无效,这绝非易事。你知道一些容易实现的东西吗?我会看看 GetTickCount,谢谢。
  • GetTickCount 简单可靠,QueryPerformanceCounter 具有更高的精度,但对于长期测量可能不准确,请参阅stackoverflow.com/questions/7583074/…
  • Now() 在 Delphi 中使用 GetLocalTime() 在其实现中提供与 GetTickCount() 相同的精度。 GetSystemTimeAsFileTime() 并没有更好,即使它返回的值是 1E-7 秒的倍数。 QueryPerformanceCounter() 是唯一可以提供帮助的,尽管它有很多问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多