我看到了一个关于为什么“民意调查不好”的问题。在最小化一个线程使用的处理器时间量方面,进行自旋等待(即轮询while循环中所需的更改)或等待内核对象(例如,windows中的内核事件对象)会更好吗? ?
对于上下文,假设代码需要在任何类型的处理器,单核,超线程,多核等上运行。还假设轮询或等待的线程无法继续,直到轮询结果令人满意如果它轮询而不是等待。最后,线程开始等待(或轮询)和满足条件之间的时间可能从很短的时间到很长的时间不等。
由于操作系统在“等待”的情况下可能更有效地“轮询”,我不希望看到“等待只是意味着其他人做投票”的论点,这是旧的消息,并不一定100%准确。
答案 0 :(得分:18)
如果操作系统具有这些类型的并发原语的合理实现,那么等待内核对象肯定会更好。
除了其他原因之外,这使操作系统知道在相同的对象处于适当的状态之前,不会为有问题的线程安排额外的时间片。否则,你有一个不断重新安排,上下文切换到,然后运行一段时间的线程。
您特别询问了最小化线程的处理器时间:在此示例中,内核对象上的线程阻塞将使用ZERO时间;轮询线程会使用各种各样的时间。
此外,“别人正在投票”的论点不一定是真的。当内核对象进入适当的状态时,内核可以在该瞬间查看哪些线程正在等待该对象...然后安排其中的一个或多个执行。在这种情况下,内核(或其他任何人)无需轮询任何内容。
答案 1 :(得分:9)
等待是“更好”的行为方式。当您在等待内核对象时,您的线程将不会被授予任何CPU时间,因为调度程序已知没有准备好工作。只有在等待条件满足时,您的线程才会获得CPU时间。这意味着你不会不必要地占用CPU资源。
答案 2 :(得分:3)
我认为尚未提出的一点是,如果你的操作系统有很多工作要做,那么阻止你的线程进入另一个进程。如果所有进程都使用阻塞原语(例如内核等待,文件/网络IO等),那么您将为内核提供更多信息来选择应运行的线程。因此,它将在相同的时间内完成更多的工作。如果你的应用程序在等待打开文件或数据包到达时可以做一些有用的事情,那么yeilding甚至可以帮助你自己的应用程序。
答案 3 :(得分:2)
等待确实涉及更多资源,意味着额外的上下文切换。实际上,一些同步原语(如CLR Monitors和Win32关键部分)使用两阶段锁定协议 - 一些自旋等待是在实际进行真正的等待之前完成的。
我认为做两阶段的事情会非常困难,并且会涉及大量的测试和研究。所以,除非你有时间和资源,坚持使用windows原语......他们已经为你做了研究。
答案 4 :(得分:2)
只有很少的地方,通常在OS低级别的东西(中断处理程序/设备驱动程序)中,旋转等待是有意义/需要的。通用应用程序总是更好地等待一些同步原语,如互斥/条件变量/信号量。
答案 5 :(得分:1)
我同意Darksquid,如果您的操作系统具有不错的并发原语,那么您不需要轮询。轮询通常在实时系统或没有操作系统的受限硬件上自带,然后你需要轮询,因为你可能没有wait()选项,但也因为它可以让你精确控制多长时间你想在特定的状态下等待,而不是受调度程序的支配。
答案 6 :(得分:1)
等待(阻塞)几乎总是最佳选择(在有效利用处理资源和最小化对同一系统上运行的其他代码的影响方面,“最佳”)。主要的例外是:
轮询通常不在操作系统内核中用于实现阻塞系统调用 - 相反,事件(中断,计时器,对互斥锁的操作)会导致阻塞的进程或线程可以运行。
答案 7 :(得分:1)
可能采取四种基本方法:
当#1实用时,它通常是最好的方法,除非延迟一个人对事件的反应可能是有益的。例如,如果一个人希望在几秒钟内收到大量的串口数据,并且如果在发送数据后100ms处理数据将与立即处理数据一样好,则使用后两者之一进行定期轮询方法可能比设置“收到数据”事件更好。
方法#3相当粗糙,但在许多情况下可能是一个很好的方法。与#1相比,它往往会浪费更多的CPU时间和资源,但在许多情况下实施起来会更简单,资源浪费在很多情况下都会小到不重要。
方法#2通常比#3更复杂,但具有能够使用单个计时器处理许多资源而没有专用线程的优势。
方法#4有时在嵌入式系统中是必需的,但除非直接轮询硬件并且在发生相关事件之前没有任何有用的东西,否则通常非常糟糕。在许多情况下,在等待它的线程产生CPU之前,等待的条件不可能发生。像方法#3那样产生CPU实际上允许等待的线程比占用它更早地看到事件。