如何清除clojure core.async频道?

时间:2015-04-26 16:03:58

标签: clojure core.async

我第一次看Clojure core.async,正在经历Rich Hickey的精彩演讲:http://www.infoq.com/presentations/clojure-core-async

我对他在演讲结束时展示的例子有疑问:

core.async Web Example

根据Rich的说法,此示例主要尝试获取特定查询的Web,视频和图像结果。它为每个结果并行尝试两个不同的源,并为每个结果提取最快的结果。并且整个操作可能不会超过80毫秒,所以如果我们不能得到例如在80毫秒的图像结果,我们只是放弃。最快的' function创建并返回一个新的通道,并启动两个go过程来检索结果并将其放在通道上。然后我们只是从最快的'中取出第一个结果。通道并将其打到c通道上。

我的问题:这三个临时的,未命名的'最快的'我们取得他们的第一个结果后的渠道据推测,仍有一个停止过程试图将第二个结果放到通道上,但是没有人在听,所以它从未实际完成。而且由于频道从未与任何东西绑定,因此我们似乎无法再对它做任何事情。请问过程&渠道"实现"没有人再关心他们的结果并清理自己了吗?或者我们基本上只是"泄漏"这段代码中有三个渠道/进程?

3 个答案:

答案 0 :(得分:3)

没有泄漏。

停放的go附加到他们尝试执行操作的通道上,并且除此之外没有独立存在。如果其他代码对频道失去兴趣,则会停靠某个go(注意。go如果alt! / {{1 alts! go可以同时成为许多频道的推杆/接收者然后最终它将与那些频道一起进行GC。

唯一需要注意的是,为了成为GC,go实际上必须首先停放。因此,任何<!在不停车(>! / alt! / alts! / {{1}})的情况下继续在循环中完成任务的{{1}}实际上将永远存在。不过,很难写出这种代码。

答案 1 :(得分:3)

除了警告和例外,您可以在REPL上测试JVM上的垃圾收集。

例如:

(require '[clojure.core.async :as async])
=> nil

(def c (async/chan))
=> #'user/c
(def d (async/go-loop [] 
         (when-let [v (async/<! c)] 
           (println v) 
           (recur))))
=> #'user/d

(async/>!! c :hi)
=> true
:hi        ; core.async go block is working

(import java.lang.ref.WeakReference)
=> java.lang.ref.WeakReference    ; hold a reference without preventing garbage collection
(def e (WeakReference. c))
=> #'user/e
(def f (WeakReference. d))
=> #'user/f

(.get e)
=> #object[...]
(.get f)
=> #object[...]

(def c nil)
=> #'user/c
(def d nil)
=> #'user/d
(println "We need to clear *1, *2 and *3 in the REPL.")
We need to clear *1, *2 and *3 in the REPL.
=> nil
(println *1 *2 *3)
nil #'user/d #'user/c
=> nil
(System/gc)
=> nil
(.get e)
=> nil
(.get f)
=> nil

刚刚发生了什么?我设置了一个go块并检查它是否正常工作。然后使用WeakReference来观察通信信道(c)和去块返回信道(d)。然后我删除了对c和d的所有引用(包括我的REPL创建的*1*2*3),请求的垃圾回收,(幸运的是,System.gc Javadoc确实如此没有强有力的保证)然后观察到我的弱参考被清除了。

至少在这种情况下,一旦删除了对相关频道的引用,频道就会被垃圾收集(无论我是否关闭它们!)

答案 2 :(得分:1)

假设fastest生成的频道只返回最快查询方法的结果,然后关闭。

如果产生了第二个结果,那么您的假设可能会导致fastest过程被泄露。他们的结果从未消耗过。如果他们依赖所有结果来终止,他们就不会终止。

请注意,如果在t子句中选择了频道alt!,也会发生这种情况。

解决此问题的通常方法是使用c关闭上一个go块中的频道close!。然后放弃对封闭通道的放置然后生产者可以终止。

问题也可以在fastest的实施中解决。在fastest中创建的流程本身可以通过alts!timeout生成,并在生成的值未在一定时间内消耗时终止。

我认为Rich没有解决幻灯片中的问题,而是支持一个不太冗长的例子。