鉴于:各种嵌套集合的复杂结构,其中refs分散在不同的级别。
需要:一种获取此类结构快照的方法,同时允许写入在其他线程中继续发生。
因此,“读者”线程需要在单个长事务中读取整个复杂状态。 “编写者”线程同时在多个短交易中进行修改。据我所知,在这种情况下,STM引擎利用了refs历史。
这里有一些有趣的结果。例如,读者在交易开始后10秒内到达一些参考。 Writer每1秒修改一次ref。它产生10个ref历史值。如果超出了参考号:max-history
的限制,则读者交易将永久运行。如果超过:min-history
,交易可能会多次重新运行。
但实际上读者只需要一个ref值(第一个),而作者只需要最近的一个。历史列表中的所有中间值都是无用的。有没有办法避免这种历史过度使用?
感谢。
答案 0 :(得分:1)
对我来说,拥有一个包含大量嵌套引用的大型结构有点“设计气味”。您正在有效地模拟可变对象图which is a bad idea if you believe Rich Hickey's take on concurrency。
尝试一些不同的想法:
答案 1 :(得分:0)
您的问题的一般答案是您需要两件事:
如果由于快照过程不够快而导致队列溢出该怎么办,那么除了优化该过程或增加队列大小之外,您无能为力。这将是一个平衡,你必须根据你的应用程序的需要进行攻击。这是一个微妙的平衡,并将根据您的系统的复杂程度进行一些非常广泛的测试。
但你走在正确的轨道上。如果您基本上将系统置于“快照写入模式”,那么您的读取器/写入器方法应该自动更改它们的读/写位置,以便正在进行更改的线程获取所有“当前值”并且线程读取快照状态是读取所有“快照值”。您可以将它们拆分为单独的方法 - 快照读取器将使用“快照值”方法,所有其他线程将读取“当前值”方法。
快照阅读器完成其工作后,需要清除快照状态。
如果线程在当前未设置“快照状态”时尝试读取“快照值”,则应仅使用“当前值”进行响应。没什么大不了的。
允许采用文件系统快照进行备份但不阻止编写新数据的系统遵循类似的方案。
最后,除非您需要记录所有更改到系统(即审计跟踪),否则事务队列实际上不需要是更改队列应用 - 它只需要存储您在系统中更改的任何内容的最新值。清除“快照状态”后,您只需将所有这些未提交的值写入系统,并将其调用完毕。您可能需要考虑的事情是记录尚未进行的更改,以防您需要从崩溃中恢复,并且仍然应用这些更改。日志文件将为您提供已发生事件的记录,并可让您执行此恢复。这是对恢复过程的过度简化,但这不是你的问题所在,所以我会停在那里。
答案 2 :(得分:0)
您所追求的是高性能并发中最先进的技术。你应该看看Nathan Bronson的工作,以及他与Aleksandar Prokopec,Phil Bagwell和Scala团队的实验室合作。
二叉树: http://ppl.stanford.edu/papers/ppopp207-bronson.pdf https://github.com/nbronson/snaptree/
基于数组树的哈希映射 http://lampwww.epfl.ch/~prokopec/ctries-snapshot.pdf
然而,快速浏览一下上面的实现应该说服你这不是"滚动你自己"领土。如果可能的话,我会尝试根据您的需求调整现成的并发数据结构。我所连接的所有东西都可以在JVM上免费获得,但它本身并不是原生的Clojure。