轮询好还是等待好?

发布于 2024-07-23 02:18:54 字数 319 浏览 11 评论 0原文

我看到一个关于为什么“轮询不好”的问题。 就最小化一个线程使用的处理器时间而言,最好进行自旋等待(即在 while 循环中轮询所需的更改)或等待内核对象(例如 Windows 中的内核事件对象) ?

对于上下文,假设代码需要在任何类型的处理器(单核、超线程、多核等)上运行。还假设要轮询或等待的线程在轮询结果令人满意之前无法继续而不是等待。 最后,线程开始等待(或轮询)与满足条件之间的时间可能会从很短的时间到很长的时间不等。

由于操作系统在“等待”的情况下可能会更有效地“轮询”,所以我不想看到“等待只是意味着其他人进行轮询”的说法,这是旧消息,并且不一定 100% 准确。

I have seen a question on why "polling is bad". In terms of minimizing the amount of processor time used by one thread, would it be better to do a spin wait (i.e. poll for a required change in a while loop) or wait on a kernel object (e.g. a kernel event object in windows)?

For context, assume that the code would be required to run on any type of processor, single core, hyperthreaded, multicore, etc. Also assume that a thread that would poll or wait can't continue until the polling result is satisfactory if it polled instead of waiting. Finally, the time between when a thread starts waiting (or polling) and when the condition is satisfied can potentially vary from a very short time to a long time.

Since the OS is likely to more efficiently "poll" in the case of "waiting", I don't want to see the "waiting just means someone else does the polling" argument, that's old news, and is not necessarily 100% accurate.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

哭泣的笑容 2024-07-30 02:18:54

如果操作系统对这些类型的并发原语有合理的实现,那么等待内核对象肯定会更好。

除其他原因外,这让操作系统知道不要为有问题的线程安排额外的时间片,直到正在等待的对象处于适当的状态。 否则,您将拥有一个不断重新调度、上下文切换然后运行一段时间的线程。

您特别询问了如何最小化线程的处理器时间:在此示例中,内核对象上的线程阻塞将使用零时间; 轮询线程会使用各种时间。

此外,“其他人正在投票”的论点不一定是真的。 当内核对象进入适当的状态时,内核可以查看当时哪些线程正在等待该对象......然后安排其中一个或多个线程执行。 在这种情况下,内核(或其他任何人)不需要轮询任何内容。

Provided the OS has reasonable implementations of these type of concurrency primitives, it's definitely better to wait on a kernel object.

Among other reasons, this lets the OS know not to schedule the thread in question for additional timeslices until the object being waited-for is in the appropriate state. Otherwise, you have a thread which is constantly getting rescheduled, context-switched-to, and then running for a time.

You specifically asked about minimizing the processor time for a thread: in this example the thread blocking on a kernel object would use ZERO time; the polling thread would use all sorts of time.

Furthermore, the "someone else is polling" argument needn't be true. When a kernel object enters the appropriate state, the kernel can look to see at that instant which threads are waiting for that object...and then schedule one or more of them for execution. There's no need for the kernel (or anybody else) to poll anything in this case.

我恋#小黄人 2024-07-30 02:18:54

等待是“更好”的行为方式。 当您等待内核对象时,您的线程不会被授予任何 CPU 时间,因为调度程序知道没有准备好的工作。 只有当满足等待条件时,您的线程才会获得 CPU 时间。 这意味着您不会不必要地占用 CPU 资源。

Waiting is the "nicer" way to behave. When you are waiting on a kernel object your thread won't be granted any CPU time as it is known by the scheduler that there is no work ready. Your thread is only going to be given CPU time when it's wait condition is satisfied. Which means you won't be hogging CPU resources needlessly.

冬天旳寂寞 2024-07-30 02:18:54

我认为尚未提出的一点是,如果您的操作系统有很多工作要做,则阻塞会将您的线程转交给另一个进程。 如果所有进程都在应该使用的地方使用阻塞原语(例如内核等待、文件/网络 IO 等),那么您将为内核提供更多信息来选择应运行哪些线程。 因此,它将在相同的时间内完成更多的工作。 如果您的应用程序在等待该文件打开或数据包到达时可以做一些有用的事情,那么 yielding 甚至可以帮助您自己的应用程序。

I think a point that hasn't been raised yet is that if your OS has a lot of work to do, blocking yeilds your thread to another process. If all processes use the blocking primitives where they should (such as kernel waits, file/network IO etc.) you're giving the kernel more information to choose which threads should run. As such, it will do more work in the same amount of time. If your application could be doing something useful while waiting for that file to open or the packet to arrive then yeilding will even help you're own app.

和我恋爱吧 2024-07-30 02:18:54

等待确实涉及更多资源,并且意味着额外的上下文切换。 事实上,一些同步原语(例如 CLR 监视器和 Win32 关键部分)使用两阶段锁定协议 - 在实际执行真正的等待之前完成一些自旋等待。

我想做两阶段的事情会非常困难,并且会涉及大量的测试和研究。 因此,除非您有时间和资源,否则请坚持使用 Windows 原语……他们已经为您进行了研究。

Waiting does involve more resources and means an additional context switch. Indeed, some synchronization primitives like CLR Monitors and Win32 critical sections use a two-phase locking protocol - some spin waiting is done fore actually doing a true wait.

I imagine doing the two-phase thing would be very difficult, and would involve lots of testing and research. So, unless you have the time and resources, stick to the windows primitives...they already did the research for you.

丑丑阿 2024-07-30 02:18:54

等待(阻塞)几乎总是最好的选择(“最佳”是指有效利用处理资源并最大限度地减少对同一系统上运行的其他代码的影响)。 主要的例外是:

  1. 当预期的轮询持续时间很短时(与阻塞系统调用的成本相似)。
  2. 主要在嵌入式系统中,当 CPU 专门用于执行特定任务并且让 CPU 空闲时没有任何好处(例如,90 年代末构建的一些软件路由器使用这种方法)。

轮询通常不在操作系统内核中使用实现阻塞系统调用 - 相反,事件(中断、计时器、互斥体上的操作)会导致阻塞的进程或线程变得可运行。

Waiting (blocking) is almost always the best choice ("best" in the sense of making efficient use of processing resources and minimizing the impact to other code running on the same system). The main exceptions are:

  1. When the expected polling duration is small (similar in magnitude to the cost of the blocking syscall).
  2. Mostly in embedded systems, when the CPU is dedicated to performing a specific task and there is no benefit to having the CPU idle (e.g. some software routers built in the late '90s used this approach.)

Polling is generally not used within OS kernels to implement blocking system calls - instead, events (interrupts, timers, actions on mutexes) result in a blocked process or thread being made runnable.

深海夜未眠 2024-07-30 02:18:54

只有少数地方,通常是在操作系统低级事物(中断处理程序/设备驱动程序)中,自旋等待有意义/是必需的。 通用应用程序最好等待一些同步原语,例如互斥体/条件变量/信号量。

There are only few places, usually within the OS low-level things (interrupt handlers/device drivers) where spin-waiting makes sense/is required. General purpose applications are always better off waiting on some synchronization primitives like mutexes/conditional variables/semaphores.

暮年 2024-07-30 02:18:54

我同意 Darksquid 的观点,如果您的操作系统具有良好的并发原语,那么您不需要轮询。 轮询通常在实时系统或没有操作系统的受限硬件上发挥作用,那么您需要轮询,因为您可能没有 wait() 选项,而且还因为它可以让您精确控制多长时间您希望在特定状态下等待,而不是受调度程序的支配。

I agree with Darksquid, if your OS has decent concurrency primitives then you shouldn't need to poll. polling usually comes into it's own on realtime systems or restricted hardware that doesn't have an OS, then you need to poll, because you might not have the option to wait(), but also because it gives you finegrain control over exactly how long you want to wait in a particular state, as opposed to being at the mercy of the scheduler.

痴梦一场 2024-07-30 02:18:54

人们可以采用四种基本方法:

  1. 使用某种操作系统等待原语来等待事件发生
  2. 使用一些操作系统定时器原语以某种定义的速率检查事件是否已经发生
  3. 反复检查事件是否已发生,但在事件未发生时使用操作系统原语生成任意且未知持续时间的时间片。
  4. 反复检查事件是否发生,如果没有发生则不释放CPU。

当#1 可行时,它通常是最好的方法,除非延迟对事件的响应可能是有益的。 例如,如果预计在几秒钟内接收大量串行端口数据,并且在发送数据后 100ms 处理数据与立即处理数据一样好,则使用后两者之一进行定期轮询方法可能比设置“收到数据”事件更好。

方法#3 相当粗糙,但在许多情况下可能是一个好的方法。 与方法 1 相比,它通常会浪费更多的 CPU 时间和资源,但在许多情况下,它会更容易实现,并且资源浪费在许多情况下足够小,无关紧要。

方法 #2 通常比方法 #3 更复杂,但具有能够使用单个计时器处理许多资源且无需专用线程的优点。

方法 #4 有时在嵌入式系统中是必要的,但通常非常糟糕,除非直接轮询硬件,并且在相关事件发生之前不会做任何有用的事情。 在许多情况下,在等待条件的线程释放 CPU 之前,等待条件不可能发生。 实际上,如方法 #3 中那样让出 CPU 将允许等待线程比占用事件更快地看到事件。

There are four basic approaches one might take:

  1. Use some OS waiting primitive to wait until the event occurs
  2. Use some OS timer primitive to check at some defined rate whether the event has occurred yet
  3. Repeatedly check whether the event has occurred, but use an OS primitive to yield a time slice for an arbitrary and unknown duration any time it hasn't.
  4. Repeatedly check whether the event has occurred, without yielding the CPU if it hasn't.

When #1 is practical, it is often the best approach unless delaying one's response to the event might be beneficial. For example, if one is expecting to receive a large amount of serial port data over the course of several seconds, and if processing data 100ms after it is sent will be just as good as processing it instantly, periodic polling using one of the latter two approaches might be better than setting up a "data received" event.

Approach #3 is rather crude, but may in many cases be a good one. It will often waste more CPU time and resources than would approach #1, but it will in many cases be simpler to implement and the resource waste will in many cases be small enough not to matter.

Approach #2 is often more complicated than #3, but has the advantage of being able to handle many resources with a single timer and no dedicated thread.

Approach #4 is sometimes necessary in embedded systems, but is generally very bad unless one is directly polling hardware and the won't have anything useful to do until the event in question occurs. In many circumstances, it won't be possible for the condition being waited upon to occur until the thread waiting for it yields the CPU. Yielding the CPU as in approach #3 will in fact allow the waiting thread to see the event sooner than would hogging it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文