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

欢迎!有关如何使用本网站的更多信息,请参阅 关于页面

+4
timrobinson33 引用、代理、原子
已关闭

如果有一个 delay 正在处理,调用 realized? 将导致调用者阻塞,直到延迟完成然后返回 true。

我预期的行为是返回 false,这在 futures 的工作方式中也是这样。毕竟,如果您想等待其完成,您会调用 deref

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

已标记为: 1.11.0-alpha2 中修复

2 答案

+1
alexmiller
感谢你这么快就注意到这件事,Alex。

昨天我也注意到,如果在REPL中尝试打印执行延迟的值而不进行解引用,也会发生阻塞。然而,我99%确信这只是因为构建字符串表示的部件同样调用了"realized?",因此我认为这不是一个不同的错误。

编辑
根据realized?的文档注释,我同意如果延迟正在进行中,返回false是正确的行为。换句话说,确实有情况我用延迟和realized?在并发环境中使用,以“延迟是否已执行?”来决定我是替换延迟(在原子操作中)还是等待已经进行中的流程完成。我可以用于解引用,但我不想在没有已进行中的流程时就启动该过程。

如果我对1.11之后的理解正确,我将无法再使用realized?来检测这种情况。是否有支持“延迟是否已执行?”的方案,或者我现在需要创建自己的构造器?

一个对所有的线程都可见的forced() / forced?谓词,一旦在deref块中进入同步块(但在thunk开始或完成之前)将是实现我要求的可能解决方案之一。
我觉得你想要的功能并不是delay(实际上IDeref / IPending)的公开API的一部分。
0

与此同时,您可以将 clojure.lang.Delay 扩展为 my.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 的,这个值的发布是安全的,并且将在所有线程中可见。
...