用单线程 LMAX 编写
Posted
技术标签:
【中文标题】用单线程 LMAX 编写【英文标题】:Writing with a single thread LMAX 【发布时间】:2015-07-26 02:32:43 【问题描述】:我已经了解了 LMAX 和这个名为 RingBuffer 的奇妙概念。 所以大家说,当只有一个线程写入环形缓冲区时,性能比多个生产者要好得多......
但是,我真的不认为典型应用程序可以仅使用一个线程在 ringbuffer 上写入...我真的不明白 lmax 是如何做到这一点的(如果他们这样做的话)。比如N个不同的交易者在交易所下单,都是异步的请求,被转化为订单并放入ringbuffer,他们怎么可能用一个线程来写呢?
问题 1. 我可能会遗漏某些内容或误解某些方面,但是如果您有 N 个并发生产者,如何将它们合并为 1 并且不相互锁定?
问题 2。我记得 rxJava observables,你可以使用 Observable.merge 将 N 个 observables 合并为 1,我想知道它是否以任何方式阻塞或维护任何锁?
【问题讨论】:
【参考方案1】:多线程写入对 RingBuffer 的影响很小,但在非常重的负载下可能会非常显着。
RingBuffer 实现包含一个next
节点,将在其中进行下一次添加。如果只有一个线程正在写入环,则该过程将始终在最短的时间内完成,即buffer[head++] = newData
。
要在避免锁定的同时处理多线程,您通常会执行while ( !buffer[head++].compareAndSet(null,newValue))
之类的操作。当其他线程干扰数据的存储时,这个紧密的循环会继续执行,从而降低吞吐量。
请注意,我在上面使用了伪代码,请查看我的实现中的getFree
here 以获取真实示例。
// Find the next free element and mark it not free.
private Node<T> getFree()
Node<T> freeNode = head.get();
int skipped = 0;
// Stop when we hit the end of the list
// ... or we successfully transit a node from free to not-free.
// This is the loop that could cause delays under hight thread activity.
while (skipped < capacity && !freeNode.free.compareAndSet(true, false))
skipped += 1;
freeNode = freeNode.next;
// ...
【讨论】:
那么对 N 个生产者(其中 n 不大)使用它会比阻塞写访问更快? @vach - 是的。任何非阻塞算法都比使用锁的算法更可取。 我无法理解 LMAX 如何在单线程中做到这一点?他们同时获得大量订单他们如何让他们在单个线程中进行环形缓冲区?对此有什么想法:)? @vach - 有一篇很棒的文章 here。只是业务逻辑是单线程的。 我已经阅读了 2 次 :) 只是觉得他们设法使生产者成为一个线程(因为他们实际上开发了它)【参考方案2】:在内部,RxJava 的合并使用我称为emitter-loop 的序列化构造,它使用synchronized
并且是阻塞的。
我们的“客户”主要在吞吐量和延迟不敏感的情况下使用合并,或者完全单线程和阻塞不是真正的问题。
可以编写我称为queue-drain 的非阻塞序列化程序,但不能将合并配置为使用它。
如果你愿意手动处理生产者和消费者线程,也可以直接看JCTools'MpscArrayQueue
。
【讨论】:
以上是关于用单线程 LMAX 编写的主要内容,如果未能解决你的问题,请参考以下文章
分布式存储引擎大厂实战——带你读源码搞懂为什么Redis用单线程还这么快
分布式存储引擎大厂实战——带你读源码搞懂为什么Redis用单线程还这么快
既然spring中注入用单例,为了解决多线程安全问题,还得用theardlocal为每个线程创建共