16 .net c# winapi multithreading sleep
我想知道调用Thread.Sleep(1)和调用SwitchToThread之间的实际区别是什么(如果我们忽略它当前没有被BCL暴露).
Joe Duffy在他的帖子中提到:
"kernel32!SwitchToThread API没有出现Sleep(0)和Sleep(1)的问题." (关于调度程序的行为)
为什么Sleep不会像SwitchToThread一样?为什么存在这种差异,以及它有什么用呢?(如果有的话......)
Ree*_*sey 23
有两点不同.第一个在SwitchToThread的MSDN文档中提到:
执行的产量仅限于调用线程的处理器.即使该处理器空闲或正在运行较低优先级的线程,操作系统也不会将执行切换到另一个处理器.
Sleep(0)也允许其他处理器上的线程运行.
SwitchToThread也只产生一个线程调度上下文.另一方面,睡眠有多种条件可供其等待.SleepEx的文档详细说明了这一点:
* An I/O completion callback function is called
* An asynchronous procedure call (APC) is queued to the thread.
* The time-out interval elapses
Run Code Online (Sandbox Code Playgroud)
这将产生多个线程.
一般来说,Sleep(0)更有可能产生一个时间片,并且即使没有其他线程在等待,它也总是屈服于OS.这就是为什么在循环中添加Sleep(0)会使处理器使用率从100%(每个核心)降至接近0%(在许多情况下).除非另一个线程正在等待时间片,否则SwitchToThread不会.
SwitchToThread() 是 Sleep(0) 的“智能”版本。它没有很好的文档记录,但据我所知,它的工作方式如下:
ready(即想要运行的线程多于可用的逻辑处理器)并且这些线程与调用 SwitchToThread() 的线程具有相同或更高的优先级,它的行为方式与 Sleep 相同(0) - 即将逻辑处理器让给这些线程之一,代价高昂的上下文切换;ready状态时,它只是退出,即调用 SwitchToThread() 的线程继续执行而无需花费任何上下文切换或 3 到环 0 转换(它不会离开用户模式)-这与 Sleep(0) 的行为方式相反,它总是将控制权交给最低优先级的线程;ready状态中没有线程时,SwitchToThread() 也像 Sleep(0)一样退出- 因此,如果您在循环中执行此操作,您只会获得当前逻辑处理器的 100% 负载,即消耗功率。Sleep(1) 与 Sleep(0) 相同,但有 1 毫秒的延迟。这 1 毫秒的延迟释放了逻辑处理器,并且不会消耗任何电源。相反,SwitchToThread 永远不会遇到任何延迟。
所以最好将 SwitchToThread 与 Sleep(0) 进行比较,而不是与 Sleep(1) 进行比较,因为 Sleep(1) 与 Sleep(0) + 1 毫秒的延迟相同。
我从“Intel 64 and IA-32 Architectures Optimization Reference Manual”和“Intel 64 and IA-32 Architectures Software Developer's Manual”中借用了一些关于这个问题的想法,它们倾向于调用一些pauseCPU 指令(也可作为内在函数)而不是SwitchToThread() 或 Sleep(0) 如果您的等待时间很短。请注意 SwitchToThread() 或 Sleep(0) 几乎立即生效,而 Sleep(1) 至少持续一毫秒。
还应考虑以下几点:
ready状态,则 SwitchToThread() 或 Sleep(0) 可能没有用,但无论是否有其他线程处于“就绪”状态,Sleep(1) 都会等待至少一毫秒。如果您的等待循环往往很短,请考虑先执行一些pauseCPU 指令。通过pause在 SwitchToThread() 或 Sleep() 调用之前使用一些CPU 指令减慢“自旋等待” ,多线程软件获得:
但是,如果您要调用 Sleep(1) ,它至少运行一毫秒,这在 CPU 周期方面非常长,那么您预计等待周期将非常长,因此pause在这种情况下指令将是徒劳的.
当等待循环预计会持续很长时间时,最好通过调用操作系统同步 API 函数之一(例如 Windows 操作系统上的 WaitForSingleObject,而不是 SwitchToThread() 或 Sleep(0) 或 Sleep() 来让步给操作系统1),因为他们在漫长的等待中非常浪费。此外,Sleep(1) 非常慢,而且像 WaitForSingleObject 或 EnterCriticalSection 这样的操作系统同步函数会反应得更快,而且它们对资源更友好。
我的结论是:最好不要使用 Sleep(0) 或 Sleep(1) 或 SwitchToThread()。不惜一切代价避免“自旋等待”循环。使用WaitForMultipleObjects()、SetEvent()等高级同步函数——它们在性能、效率和节能方面是最好的。尽管它们也遭受昂贵的上下文切换和环 3 到环 0 的转换,但与您在使用 Sleep() 或 SwitchToThread() 的“自旋等待”循环中所花费的费用相比,这些费用很少见并且非常合理。
在支持 HT 技术的处理器上,自旋等待循环会消耗处理器执行带宽的很大一部分。一个执行自旋等待循环的逻辑处理器会严重影响另一个逻辑处理器的性能。这就是为什么有时禁用 HT 可能会提高性能的原因。
持续轮询设备或文件或其他数据源以进行状态更改会导致计算机消耗更多电量,给内存和系统总线带来压力,并提供不必要的页面错误(使用 Windows 中的任务管理器查看哪个应用程序在空闲时产生大多数页面错误 - 这些是效率最低的应用程序,因为它们使用“轮询”)。尽可能减少轮询,并使用事件驱动的方式编写应用程序。这是我强烈推荐的最佳实践。您的应用程序应该一直处于睡眠状态,等待预先设置的多个事件。事件驱动应用程序的一个很好的例子是 Linux 下的 Nginx。以轮询电源更改为例。如果操作系统为各种设备状态更改提供通知服务(甚至 WM_ 消息),例如将电源从交流电源转换为电池,请使用这些通知服务而不是轮询设备状态更改。这种方法减少了代码轮询电源状态的开销,因为代码可以在状态发生变化时异步获取通知。
与某些人所写的相反,Sleep(0) 不会将 CPU 消耗降低到接近零。它将执行释放给处于“就绪”状态的其他线程,但如果没有这样的线程,它只会浪费数千个 CPU 周期并消耗当前线程的 100% CPU 周期,正如stackoverflow 成员所证明的那样- 和我也刚刚再次检查了这一点 - Sleep(0) 循环在 Windows 10 64 位上消耗了当前线程的 100% CPU。
| 归档时间: |
|
| 查看次数: |
6534 次 |
| 最近记录: |