我最近听到一些开发人员说他们只是轮询一些东西(数据库、文件等)以确定什么时候发生了变化,然后运行一个任务,比如导入。
我真的反对这个想法,并认为利用可用的技术,如Remoting、WCF等会比轮询好得多。
但是,我想找出其他人更喜欢一种方法而不是另一种方法的原因,更重要的是,我怎样才能说服其他人在这个时代投票是错误的?
轮询本身并不是“错误的”。
很大程度上取决于它的实施方式和目的。如果您真的关心立即通知更改,那将非常有效。您的代码处于紧密循环中,不断轮询(询问)资源是否已更改/更新。这意味着您会尽快收到通知,告知您有所不同。但是,您的代码没有做任何其他事情,并且在对相关对象的许多调用方面存在开销。
如果您不太关心即时通知,则可以增加轮询之间的间隔,这也可以很好地工作,但选择正确的间隔可能很困难。太长,您可能会错过关键更改,太短,您又回到第一种方法的问题。
在这些情况下,诸如中断或消息等替代方案可以提供更好的折衷方案。您会在实际可能的情况下尽快收到更改通知,但是这种延迟不是您可以控制的,它取决于组件本身是否及时传递状态更改。
投票有什么“错误”?
但...
在这个时代使用轮询的例子:
有时根本无法获得异步通知:例如,用推送系统替换 RSS,服务器必须了解阅读提要的每个人并有联系他们的方式。这是一个邮件列表——这正是 RSS 旨在避免的事情之一。因此,我的大多数示例都是网络应用程序,这很可能是一个问题。
其他时候,即使有异步通知,轮询也足够便宜。
对于本地文件,更改通知原则上可能是更好的选择。例如,如果您永远戳它,您可能(可能)阻止磁盘旋转,尽管操作系统可能会再次缓存。如果您每秒轮询一个每小时只更改一次的文件,您可能会不必要地占用机器处理能力的 0.001%(或其他)。这听起来很小,但是当您需要轮询 100,000 个文件时会发生什么?
但是,在实践中,无论您做什么,开销都可能可以忽略不计,因此很难对更改当前有效的代码感到兴奋。最好的办法是注意轮询在您要更改的系统上导致的特定问题 - 如果您发现任何问题,请提出这些问题,而不是试图提出反对所有轮询的一般论点。如果你没有找到任何东西,那么你就无法修复没有损坏的东西......
轮询原则上被认为是不好的原因有两个。
这是对资源的浪费。您很可能会在未发生更改时检查更改。花费在此操作上的 CPU 周期/带宽不会导致更改,因此本可以更好地花费在其他方面。
轮询是在一定的时间间隔内完成的。这意味着在下一次间隔过去之前,您不会知道发生了更改。
最好通知更改。这样,您就不会轮询尚未发生的更改,并且您会在收到通知后立即知道更改。
轮询很容易做到,非常容易,就像任何程序代码一样简单。不轮询意味着您进入了异步编程的世界,这并不容易脑残,有时甚至可能变得具有挑战性。
与任何系统中的所有事物一样,通常更常采用阻力较小的路径,因此总会有程序员使用轮询,甚至是优秀的程序员,因为有时不需要使用异步模式使事情复杂化。
我总是喜欢避免轮询,但有时我还是会进行轮询,尤其是当异步处理的实际收益不是那么好时,例如在处理一些小的本地数据时(当然你会更快一点,但是用户在这种情况下不会注意到差异)。因此,恕我直言,这两种方法都有空间。
客户端轮询的扩展性不如服务器通知。想象一下成千上万的客户向服务器询问“有什么新数据吗?” 每 5 秒。现在想象一下服务器保存一个客户端列表来通知新数据。服务器通知可以更好地扩展。
我认为人们应该意识到,在大多数情况下,即使在事件或中断驱动的情况下,也会在某种程度上进行轮询,但是您与执行轮询的实际代码是隔离的。真的,这是最理想的情况……将自己与实施隔离开来,只处理事件。即使您必须自己实现轮询,也要编写代码以使其隔离,并且独立于实现来处理结果。
关于投票的事情是它有效!它可靠且易于实施。
池化的成本可能很高——如果您在每天只有两次更改的情况下每分钟扫描数据库以查找更改,那么您将消耗大量资源以获得非常小的结果。
然而,任何通知技术的问题在于,它们的实现要复杂得多,而且它们不仅不可靠,而且(这是一个很大的问题)你无法轻易判断它们何时不工作。
因此,如果您确实放弃了对其他技术的轮询,请确保它可供普通程序员使用并且非常可靠。
它的简单 - 轮询很糟糕 - 效率低下,浪费资源等。即使没有选择“轮询”,也总是存在某种形式的连接来监视某种事件。
那么,为什么要加倍努力并进行额外的投票。
回调是最好的选择 - 只需要担心将回调与您当前的流程联系起来。在底层,正在进行轮询以查看连接是否仍然存在。
如果你一直打电话/给你的女朋友打电话,而她从来没有接听,那为什么还要继续打电话呢?只需留言,等她“回电”;)
我偶尔会在某些情况下使用轮询(例如,在游戏中,我会每帧轮询键盘状态),但绝不会在仅进行轮询的循环中进行轮询,而是将轮询作为检查(资源 X 是否已更改?如果是的,做一些事情,否则处理其他事情并稍后再检查)。不过一般来说,我避免轮询支持异步通知。
原因是我不会花费资源(CPU 时间等)来等待某事发生(尤其是如果这些资源一开始就可以加速那件事的发生)。在我使用轮询的情况下,我不会闲着等待,我会在其他地方使用资源,所以这不是问题(至少对我而言)。
如果您正在轮询对文件的更改,那么我同意您应该使用在发生这种情况时可用的文件系统通知,这些通知现在在大多数操作系统中都可用。
在数据库中,您可以触发更新/插入,然后调用您的外部代码来执行某些操作。但是,可能只是您不需要即时操作。例如,您可能只需要在 15 分钟内将数据从不同网络上的数据库 A 获取到数据库 B。数据库 B 可能无法从数据库 A 访问,因此您最终会从数据库 B 或作为在数据库 B 附近运行的独立程序进行轮询。
此外,轮询是一种非常简单的编程方式。它通常是在时间限制很短时完成的第一步实施,并且因为它运行得很好,所以它仍然存在。
我在这里看到很多答案,但我认为最简单的答案是它自己的答案:
因为(通常)编写轮询循环比为回调创建基础结构要简单得多。
然后,您会得到更简单的代码,如果它后来成为瓶颈,可以很容易地理解并重新设计/重构为其他东西。
这不是在回答你的问题。但实际上,尤其是在这个处理器周期很便宜、带宽很大的“时代”,轮询对于某些任务来说实际上是一个很好的解决方案。
好处是:
我同意避免投票是一个好政策。但是,参考Robert 的帖子,我想说的是,在此处提到的问题不是那么大的问题的情况下,轮询的简单性可以使其成为一种更好的方法,因为异步方法通常可读性差且难以维护,更不用说可能潜入其实施的错误。
与所有事情一样,这取决于。我工作的一个大型高事务系统目前使用 SQL 通知(在 SQL Server 中加载的 DLL,由扩展 SP 从某些表上的触发器调用。DLL 然后通知其他应用程序有工作要做)。
然而,我们正在远离这一点,因为我们实际上可以保证会有工作持续进行。因此,为了降低复杂性并实际加快速度,应用程序将处理它们的工作并立即再次轮询数据库以获取新工作。如果没有,它会在一小段时间后重试。
这似乎工作得更快,也更简单。然而,应用程序的另一部分体积小得多,并不能从使用这种方法的速度提高中受益——除非轮询间隔非常小,这会导致性能问题。所以我们在这部分保持原样。所以合适的时候是好事,但是每个人的需求不一样。
以下是推拉的相对优点的一个很好的总结: https ://stpeter.im/index.php/2007/12/14/push-and-pull-in-application-architectures/
我希望我可以将它进一步总结到这个答案中,但有些事情最好不要删节。
在考虑 SQL 轮询时,在 VB6 时代,您曾经能够使用 WithEvents 关键字创建记录集,这是异步“侦听”的早期化身。
我个人总是会在轮询之前寻找一种使用事件驱动实现的方法。未能手动实施以下任何一项可能会有所帮助:
其中一些可能需要对您的项目进行轻微的重新设计,但在企业世界中可能是比轮询服务更好的途径。
同意异步/消息传递通常更好的大多数响应。我完全同意罗伯特古尔德的回答。但我想再补充一点。
一个补充是投票可以用一块石头杀死两只鸟。在一个特定的用例中,我参与的一个项目使用了数据库之间的消息队列,但从应用程序服务器轮询到其中一个数据库。由于从应用服务器到数据库的网络偶尔会出现故障,因此额外使用轮询来通知应用网络问题。
最后,考虑到可扩展性,使用对用例最有意义的东西。
我正在使用轮询来检查文件的更新,因为我在具有不同操作系统类型的异构系统中获取有关该文件的信息,其中一种非常旧。如果文件位于具有不同操作系统的远程系统上,Linux 的通知将不起作用,因为该信息不会传输,但轮询有效。这是一个低带宽检查,所以它不会伤害任何东西。