2024 Clojure现状调查!中分享您的想法。

欢迎!请查阅关于页面了解更多关于其工作方式的信息。

+4
Refs, agents, atoms
已关闭

如果当前正在处理Delay,调用realized?将导致调用者阻塞,直到Delay完成然后返回true。

我期望的行为应该是返回false,这与futures的行为一致。毕竟,如果你想等待它完成,你应该调用它的deref

(另外,似乎没有与并发编程相关的标签或类别,但鉴于Delay是clojure.core的一部分,我假设这里是正确提问的地方)

附带注释关闭: 在1.11.0-alpha2中修复

2个回答

+1
谢谢你如此迅速地处理这个问题,Alex。

昨天我还注意到,就在REPL中尝试打印执行延迟的值,而不去解引用它,也会阻塞。然而,我有99%的把握,这仅仅是因为构建字符串表示形式的代码也调用了"realized?",因此我认为这不是另一个不同的错误。

编辑
根据realized?的文档字符串,我同意,如果延迟仍在途中,返回false是正确的行为。但是,确实存在这样的时刻,我在并发环境中使用延迟和realized?来了解“延迟是否已强制执行?”以决定我是要替换延迟(在atom中)还是等待已经进行中的过程完成。我可以用deref来实现这一点,然而,我不想如果它尚未开始行驶就启动这个过程。

如果我的理解正确,1.11之后我不能再使用realized?来检测这种情况。是否存在一个支持回答“延迟是否已强制执行?”的方法,或者现在我需要创建自己的结构体?

在deref中同步块开始时(但在thunk启动/完成之前),变得对所有线程可见的第二个isForced() / forced?谓词是解决我请求的一个可能的方案。
我认为你想要的功能不是delay(实际上是IDeref / IPending)的公共API的一部分。
0

在此同时,您可以扩展 clojure.lang.Delaymy.Delay,并重写 isRealized 使其不是 synchronized,并拥有一个新的延迟。

(defmacro my-delay [& body]
  (list 'new 'my.Delay (list* `^{:once true} fn* [] body)))
我认为,由于 `realized?` 和 `force` 已不再同步,延迟提供的并发原语服务已经改变。现在在任务尚未完成的情况下,多个线程的多个调用者可能会同时 `force` 延迟。这意味着在缓存之前,thunk 可能会被调用多次。
去掉对 isRealized() 的 synchronized 并不影响跨线程的 deref 代码或其语义(在 deref() 中仍然是同步的)。

由于 fn 是 volatile 的,发布此值的操作是安全的,并且将跨线程被看到。
...