请在 2024 年 Clojure 状态调查! 中分享您的想法。

欢迎!请参阅 关于 页面获取更多关于如何使用此页面的信息。

+4
Refs, agents, atoms
关闭

如果 delay 当前正在处理,调用 realized? 将会导致调用者阻塞,直到延时完成并返回 true。

我预期的行为是它应该返回 false,与 futures 一样。毕竟,如果你想等待它完成,你会使用 deref

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

备注:已固定在 1.11.0-alpha2

2 个答案

+1
感谢你如此迅速地回复,Alex。

昨天我还注意到,在REPL中尝试打印执行延迟的值而不进行解引用也会阻塞。然而,我99%确信这仅仅是因为构建字符串表示的任何东西都在调用"realized?",所以我认为这不是不同的错误。

编辑了
根据realized?的文档字符串,我同意如果延迟目前正在执行,则返回false是正确的行为。然而,确实有我使用延迟和realized?在并发上下文中以确定“延迟是否被强制”的时候,以便决定是否要在原子操作中替换延迟(或等待正在执行的过程完成)。我能用deref来做这件事,但是我不希望在没有正在执行的情况下启动进程。

如果我对1.11之后的理解正确的话,我将无法再使用realized?来检测这种情况。有没有一种支持的方式来回答“延迟已经被强制了吗?”或者我现在需要创建自己的结构吗?

A第二个isForced() / forced?谓词,在deref中的同步块进入时(但在thunk开始/完成后可见)变为true,这是一个可能的解决方案之一,以回答我提出的问题。
我认为你想要的不是delay(实际上是IDeref / IPending)的公共API的一部分。
0

与此同时,您可以将 clojure.lang.Delay 扩展为 my.Delay 并覆盖 isRealized 以取消同步,并拥有一个新的延迟

(defmacro my-delay [& body]
  (list 'new 'my.Delay (list* `^{:once true} fn* [] body)))
我认为由于 `realized?` 和 `force` 且不再同步,所以延迟提供并发原语服务已发生变化。现在,如果有多个线程尚未完成工作,多个调用者可以潜在地同时触发延迟,这意味着在缓存之前thunk可能被调用多次。
取消在 isRealized() 上的同步并不会更改线程之间 deref 代码及其语义(deref() 仍然是在同步中)。

由于 fn 是 volatile 的,所以发布此值是安全的,并且将在线程之间可见。
...