【问题标题】:How to avoid 100% CPU utilization without removing while(true)如何在不删除 while(true) 的情况下避免 100% CPU 利用率
【发布时间】:2013-02-07 23:57:34
【问题描述】:

我正在开发一个需要一段时间(true)的系统,其中循环不断侦听队列并增加内存中的计数。

数据不断地进入队列,所以我无法避免 while(true) 条件。但自然它会将我的 CPU 利用率提高到 100%。

那么,我怎样才能让一个线程保持活动状态,它侦听队列尾部并执行一些操作,但同时将 CPU 利用率降低到 100%?

【问题讨论】:

  • while (true) {} 由于 while 循环而占用大量 CPU。 while (true) { /* lot of processing */ } 由于体内发生的事情而占用大量 CPU。不同的问题。
  • 如果它进入队列,使用阻塞队列等待下一个元素到达。
  • 这个线程发生了什么?
  • 问题:“我可怜的不断轮询设计要求我使用 100% 的 CPU。我怎么能不使用 100% 的 CPU? i>”回答:“改变你的设计。不要经常轮询。”
  • @Nik 我同意,需要重新考虑我处理通过队列传入的恒定数据流的方式。

标签: java multithreading


【解决方案1】:

Blocking queues 正是为此目的而发明的。

另见:What are the advantages of Blocking Queue in Java?

【讨论】:

  • 但是我如何在这里使用 BlockingQueue 呢?我有一个基于 hsqldb 构建的持久队列实现。我从持久队列中接收()消息。所以,我的 while 循环一直在轮询队列。
  • @zengr:我们必须查看您的代码。这个想法是等待某事发生,而不是不断检查它是否已经发生。您只需尽可能快地一遍又一遍地检查队列,这会最大限度地利用资源。
  • 如果您正在轮询数据库以查找更改(不知道更多关于您在做什么 - “队列”的坏主意),那么您将不得不 sleep() 而不是不断轮询。
  • @Brian Roach 好吧,我同意。但是 hsqldb 的行为有点不同。它在内存中保存数据并维护日志,在进程重新启动时使用该日志重建数据。所以,我正在轮询一个数据库,是的,但是“选择”在内存中。这还不错。
  • @zengr:内存中的数据更糟。如果它必须通过网络访问数据库,那至少会减慢它的速度并减少它的资源消耗,因为它至少会在一段时间内等待来自数据库的响应。由于它全部在内存中,它从不等待任何东西,只是以可以想象的最大速度燃烧资源。
【解决方案2】:

LinkedBlockingQueue.take() 是您应该使用的。这会等待一个条目到达队列,不需要额外的同步机制。

(Java,IIRC 中还有一两个其他阻塞队列,但它们的特性使其不适用于一般情况。不知道为什么如此重要的机制被埋在神秘的类中如此之深。)

【讨论】:

    【解决方案3】:

    通常队列有办法从中检索项目,并且您的线程将被取消调度(因此使用 0% cpu),直到队列中出现某些内容...

    【讨论】:

      【解决方案4】:

      根据您对另一个答案的 cmets,您希望有一个基于 hsqldb 更改的队列

      出现了一些快速的谷歌搜索:

      http://hsqldb.org/doc/guide/triggers-chapt.html

      看来您可以对其进行设置,以便更改导致发生触发器,该触发器将通知您编写的实现org.hsqldb.Trigger 接口的类。让该类包含对 Java 中 Concurrent 包中的 LinkedBlockingDequeue 的引用,并让触发器将更改添加到队列中。

      您现在有一个阻塞队列,您的读取线程将阻塞该队列,直到 hsqldb 触发触发器(来自写入器的更新),该触发器会将某些内容放入队列中。然后等待的线程将解除阻塞并将项目从队列中移出。

      【讨论】:

      • +1 用于 DB 触发器。我不熟悉该数据库,但如果支持,这是最好的答案。
      • @Brian 您对此有何看法:stackoverflow.com/a/14764347/231917
      • @zengr - 是的,这行得通。在没有数据的情况下,就 CPU 利用率而言,您只会“浪费”亚毫秒范围内的东西。如果您的要求允许数据有 10 秒的历史,那绝对没问题。
      • 是的,10 秒延迟是可以接受的。它是一个记录系统健康“滴答”的日志收集器。
      【解决方案5】:

      lbalazscs 和 Brain 有很好的答案。我无法分享我的代码,他们很难为我的问题提供确切的解决方案。并且有一段时间(true)不断地轮询队列肯定是错误的方法。所以,这就是我所做的:

      1. 我使用ScheduledExecutorService 延迟了 10 秒。
      2. 我读取了一个消息块(比如 10k)并处理这些消息
      3. 线程再次被调用,“循环”继续。

      这大大减少了我的 CPU 使用率。欢迎提出建议。

      【讨论】:

        【解决方案6】:

        阅读书籍并且只在学校浪费时间的人提供了很多愚蠢的答案,而不是我看到的直接逻辑或答案。

        while(true) 会将您的程序设置为使用基本上由 Windows 算法“分配”给它的所有 CPU 能力来运行循环中的内容,通常一遍又一遍地尽可能快地运行。这并不意味着如果它在您的应用程序上显示 100%,如果您运行游戏,您的空循环 .exe 将占用您所有的操作系​​统 CPU 资源,游戏仍应按预期运行。它更像是一个视觉错误,类似于windows空闲进程和其他一些进程。解决方法是添加 Sleep(1)(至少 1 毫秒)或更好的 Sleep(5),以确保其他东西可以运行并确保 CPU 不会尽可能快地不断循环您的 while(true)。这通常会将视觉队列中的 CPU 使用率降至 0% 或 1%,因为 1 毫秒对于更老的 CPU 来说是一个很大的休息时间。

        很多时候,while(trues) 或通用的无限循环是糟糕的设计,并且可以大大减慢到甚至 Sleep(1000) - 1 秒间隔检查或更高。无限循环并非总是糟糕的设计,但通常可以改进..

        有趣的是,我在 12 岁学习 C 时发现了这个错误,并给出了所有“愚蠢”的答案。

        只要你尝试过就知道了,除非你学会使用的脚本化的较慢的语言本身已经将它固定在某个地方,否则当操作系统实际运行时,windows 会声称在执行空循环时会使用大量 CPU免费资源可供使用。

        【讨论】:

        • 我相信你的意思是傲慢和虚荣,但我不确定。
        猜你喜欢
        • 1970-01-01
        • 2016-04-08
        • 2014-03-23
        • 1970-01-01
        • 2015-11-26
        • 1970-01-01
        • 2020-05-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多