10个元素



100个元素



1000个元素



10000个元素




评论由:alexmiller发表

评论由:tonsky发表




- 是否期望从多个线程使用变换器?因为现有的实施例明显存在竞态条件。我想修复这个问题会很昂贵(我们需要同步区域),所以也许应该有一个专用变换器,你只在需要时使用它?

评论由:alexmiller发表

瞬态集不能原地修改 - 您必须使用返回值。

是的,变换器可以在多个线程中使用(例如,在 core.async 的 go 块中的变换器通道中)。

评论由:alexmiller发表

我也应该说明,变换器不应期望同时从超过一个线程使用,因此不存在竞态问题。但随着时间的推移,从多个线程使用需要适当的常数公布。

评论由:tonsky发表

bq. 但随着时间的推移,从多个线程使用需要适当的常数公布。

这是否意味着在变换器中不能使用任何瞬态(因为瞬态实现所基于的底层数组是原地修改的,所以不同的线程可能会看到瞬态对象的不同状态)?

这是否也意味着 {{partition-by}} 和 {{partition-all}} 应该修复(它们使用 {{java.util.ArrayList}},由于是引用数组,没有常数公布的语义)?

bq. 瞬态集不能原地修改 - 您必须使用返回值。

我考虑了一下,{{clojure/core.clj}} 和 {{clojure.lang.ATransientSet.java}} 都是 Clojure 内部的一部分,位于同一位置,所以可以稍微共享一些彼此的内知道。这似乎是安全的,因为这些知识不会外漏,并且如果在某个时候 ATransientSet 的实现会发生变化,core.clj 可以在同一个版本中相应更新。当然,我不会在第三方库中这样做。

评论由:alexmiller发表

{quote}这是否意味着在变换器中不能使用瞬态(因为瞬态实现所基于的底层数组是原地修改的,所以不同的线程可能会看到瞬态对象的不同状态)?{quote}

瞬态变量只需要确保一次不会被多个线程同时请求,因此可以在转换器中使用。然而,它们应保证安全发布。core.async 通道作为其实施的一部分已经做到了这一点,但其他转换器环境可能并不如此。

无论并发性如何,瞬态变量永远不应该用作“原地修改”。尽管在某些情况下它们可能看似“工作”良好,但这永远是不正确的(最终更新操作将返回一个新实例,如果您正在原地修改,则您的数据将丢失)。关于此以及正确示例的讨论,请参阅https://clojure.org/reference/transients

{quote}这难道也意味着 partition-by 和 partition-all 应该被修复(它们使用 java.util.ArrayList,作为引用数组,没有安全发布的语义)吗? {quote}

这是我和 Rich 讨论的话题,但可能。

评论由:tonsky发表

[~alexmiller] 这是一个快速测试,它显示在一个线程中修改瞬态集合(实际上仅仅是瞬态映射的包装)的更改在某些情况下可能不被另一个线程看到。

https://gist.github.com/tonsky/62a7ec6d539fc013186bee2df0812cf6

这意味着如果我们尝试使用瞬态变量例如来处理 distinct,则可能会遗漏重复项

由:tonsky 评论

从 distinct 的转换器算术中删除了瞬态变量,因为转换器可能从多个线程访问

评论由:tonsky发表

也许该文档https://clojure.org/reference/transients应关于瞬态从多个线程使用不是安全的进行更新,因为一个线程所做的更改不一定对另一个线程可见。即使它们没有竞争。

评论由:alexmiller发表

我认为这个测试展示了瞬态集合/映射中的一个错误,您应该为这个错误创建一个问题单,因为这比这个增强更为重要。

distinct 应该能够在转换器和懒惰序实现中使用瞬态变量。contains? 在瞬态变量上不工作的问题实际上是一个单独的条目 - http://dev.clojure.org/jira/browse/CLJ-700,这可能需要一些类层次结构的重新排列。我认为我们不会在解决这个问题之前进行这个更改,这样您就可以避免依赖类和 Java 方法变体。

评论由:tonsky发表

我必须承认,我的测试表明了其他事物:没有适当的线程隔离。因此,这是一个并发问题,而不是“安全发布”问题。我目前理解的是这样的

bq. 原子类型需要线程隔离。特定原子类型的实例使用应该由将其使用在单线程作用域内或使用强制此规则的框架来控制。

这个保证隐含地假设多个线程之间的原子类型使用存在“存在之前”的关系。没有其他方法可以定义“一次只有一个线程在这个部分”。

这意味着在1号线中发生的所有写入在2号线中都是可见的,不管涉及变量的易变性如何。实际上,我们可以从原子类型的实现中移除所有易变性,可能使它们运行更快,因为我们通过“一次不超过一个线程”来强制用户在部分之间建立“存在之前”,这将会为我们提供我们需要的所有安全发布保证。

我的理解正确吗?我遗漏了什么吗?

由:tonsky 评论

此外,长期存在的原子类型(例如,与队列关联的transducer中的原子类型)将持有创建它们的线程的引用。这是好事吗?我们应该改为使用布尔标志吗?

...