【问题标题】:CPU usage is 100% during Thread.onSpinWait()在 Thread.onSpinWait() 期间 CPU 使用率为 100%
【发布时间】:2020-08-01 13:12:18
【问题描述】:

我正在为我的加密交易机器人编写回测原始数据收集器,但遇到了一个奇怪的优化问题。

我在 Executors.newCachedThreadPool() 中经常有 30 个可运行对象,运行来自 API 的 get 请求。由于 API 的请求限制为每分钟 1200 个,因此我的可运行文件中有这段代码:

    while (minuteRequests.get() >= 1170) {
        Thread.onSpinWait();
    }

是的,minuteRequests 是一个 AtomicInteger,所以我没有遇到任何问题。

一切正常,问题是即使我使用推荐的忙等待 onSpinWait 方法,当启动等待时,我的 CPU 使用率从 24% 左右上升到 100%。作为参考,我在 3900X(24 线程)上运行它。

关于如何更好地处理这种情况的任何建议?

【问题讨论】:

    标签: java multithreading performance concurrency wait


    【解决方案1】:

    我的建议是不要忙着等待


    javadocsThread.onSpinWait 说这个:

    表示调用者暂时无法继续,直到其他活动发生一个或多个操作。通过在自旋等待循环构造的每次迭代中调用此方法,调用线程向运行时指示它正忙于等待。运行时可能会采取措施提高调用自旋等待循环构造的性能

    请注意,突出显示的部分使用了 ma​​y 而不是 will。这意味着它也可能不会做任何事情。此外,“提高性能”并不意味着您的代码会客观高效。

    javadoc 还暗示改进可能取决于硬件。

    简而言之,这是使用onSpinwait 的正确方法...但是您对它的期望过高。它不会使您的忙等待代码高效。


    那么我会建议你实际做什么?

    我建议您将 AtomicInteger 替换为 Semaphore (javadoc)。此特定循环将替换为以下内容:

    semaphore.acquire();
    

    这会阻塞1,直到有 1 个“许可”可用并获得它。有关信号量如何工作的说明,请参阅类 javadocs。

    注意:由于您没有向我们展示您的速率限制的完整实现,因此尚不清楚您当前的方法实际上是如何工作的。因此,我无法准确告诉您如何将AtomicInteger 替换为Semaphore


    1 - 被阻塞的线程被“停放”,直到其他线程释放许可。当它被停放时,线程不运行并且不与 CPU 内核相关联。内核要么处于空闲状态(通常处于低功耗状态),要么被分配给其他线程。这通常由操作系统的线程调度程序处理。当另一个线程释放许可时,Semaphore.release 方法将告诉操作系统解除在acquire 中阻塞的线程之一。

    【讨论】:

    • 它不仅解决了问题,而且看起来也好多了。您介意解释一下这种情况下的“阻塞”有何不同以及为什么它不会占用 CPU 时间吗?据我了解,这与停放线程和等待线程之间的区别有关?
    • @PressFToMakeGames 停放和等待之间没有区别。您的循环只是在线程可运行、未阻塞且未等待时执行的代码。
    猜你喜欢
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多