11

在我的应用程序中,有几个服务在它们自己的线程上处理信息,当它们完成后,它们会将消息发布到下一个服务,然后继续在自己的线程上完成工作。消息的移交是通过 LinkedBlockingQueue 完成的。切换通常需要 50-80 us(从将消息放入队列直到消费者开始处理消息)。为了加快最重要服务的移交,我想使用繁忙的自旋而不是阻塞方法(我有 12 个处理器内核并希望将 3 个专用于这些重要服务)。所以..我将 LinkedBlockingQueue 更改为 ConcurrentLinkedQueue

并且做了

for(;;)
{
 Message m = queue.poll();
 if( m != null )
  ....
}

现在..结果是第一个消息传递需要 1 我们,但随后延迟在接下来的 25 次切换中增加,直到达到 500 我们,然后延迟突然回到 1 我们并开始增加..所以我有延迟具有 25 次迭代的周期,其中延迟从 1 us 开始,到 500 us 结束。(消息每秒传递大约 100 次)

平均延迟为 250,这并不是我想要的性能提升。

我还尝试使用 LMAX Disruptor ringbuffer 而不是 ConcurrentLinkedQueue。该框架在繁忙的自旋实现中具有自己的构建和完全不同的队列实现,但结果是相同的。所以我很确定这不是队列的错,也不是我滥用某些东西..

问题是..这到底是怎么回事?为什么我会看到这种奇怪的延迟周期?

干杯!!

4

4 回答 4

1

据我所知,如果线程调度程序检测到该线程正在大量使用 CPU,它可以故意将线程暂停更长的时间——以便更公平地在不同线程之间分配 CPU 时间。尝试LockSupport.park()在队列为空后添加消费者,并LockSupport.unpark()在添加消息后添加生产者 - 这可能会使延迟变小;不过,与阻塞队列相比,它是否真的会更好是一个大问题。

于 2013-01-04T13:04:30.273 回答
0

如果您真的需要按照您描述的方式完成工作(而不是 Andrey Nudko 在 1 月 5 日 13:22 回复的方式),那么您肯定还需要从其他角度看待问题。

只是一些提示:

  1. 尝试检查您的整体环境(在 JVM 之外)。例如:

  2. JVM 内部的“问题”

    • 垃圾收集器(尝试不同的:http ://www.oracle.com/technetwork/java/gc-tuning-5-138395.html#1.1.%20Types%20of%20Collectors%7Coutline )
  3. 尝试更改线程优先级:设置 Java 线程的优先级

于 2013-03-16T20:09:07.427 回答
0

这只是疯狂的猜测(因为正如其他人所提到的,您没有收集有关队列长度、失败的轮询、空轮询等的任何信息):

我使用了力量并阅读了ConcurrentLinkedQueue 的来源,或者更确切地说,简要地浏览了一两分钟。轮询并不是您微不足道的 O(1) 操作。可能是您正在遍历多个已过时的节点,保持为空;并且可能存在额外的临时状态,这些状态涉及连接到自身的节点作为下一个节点,作为队列陈旧/移除的指示。可能是由于线程调度,队列开始堆积垃圾。尝试按照代码中提到的抽象算法的链接:

Maged M. Michael 和 Michael L. Scott的简单、快速和实用的非阻塞和阻塞并发队列(链接有 PDF 和伪代码)。

于 2013-04-04T17:31:01.857 回答
0

这是我的 2 美分。如果您在基于 linux/unix 的系统上运行,则有一种方法可以将某个 cpu 专用于某个线程。本质上,您可以让操作系统忽略该 cpu 进行任何调度。检查 cpu 的隔离级别

于 2015-11-12T23:56:11.220 回答