考虑从example walkthrough of core.async获取的以下代码:
(let [c1 (chan)
c2 (chan)]
(thread
(while true
(let [[v ch] (alts!! [c1 c2])]
(println "Read" v "from" ch))))
(>!! c1 "hi")
(>!! c2 "there"))
我的假设是线程引用了两个通道c1
和c2
,并且基本上会一直运行,试图从任何永远不会出现的值中获取值。因此,通道也不会被垃圾收集,线程也不会终止。即使我们明确地close!
频道,线程仍将继续。我的结论是正确的还是我错过了什么?
我在问,因为我正试图找到一种方法,让我可以成功地测试这样的core.async代码与这样一个无休止地运行的消费者。我目前的尝试看起来像这样:
(let [c1 (chan)
c2 (chan)]
(go
(>!! c1 "hi")
(>!! c2 "there"))
(async/thread
(loop [[v ch] (alts!! [c1 c2])]
(println "Read" v "from" ch)
(when-let [[nv nch] (alts!! [c1 c2])]
(if nv
(recur [nv nch])
:done)))))
这会返回一个结果通道(来自thread
),我想阻止它取:done
值,但我需要一种方法来关闭(至少有一个)通道。我可以返回两个频道c1, c2
的列表以及thread
然后close!
返回的结果频道,例如之后c1
检查结果频道,但这非常难看:
(let [c1 (chan)
c2 (chan)]
(go
(>!! c1 "hi")
(>!! c2 "there"))
[c1 c2 (async/thread
(loop [[v ch] (alts!! [c1 c2])]
(println "Read" v "from" ch)
(when-let [[nv nch] (alts!! [c1 c2])]
(if nv
(recur [nv nch])
:done))))])
=> [#<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@60eb5def> #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@7c64279e> #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@136535df>]
Read hi from #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@60eb5def>
Read there from #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@7c64279e>
(let [[c1 c2 resultchan] *1]
(close! c1)
(<!! resultchan))
=>:done
或者,我可能会发送一个特殊的“通信结束”值,然后我可以在接收方检查。
最佳做法是什么样的?
答案 0 :(得分:1)
我不知道是否有针对这种特殊情况的最佳做法。这是我解决问题的方法。我认为这很简单。
(defn alts-while-open [f & chans]
(let [a-chans (atom (set chans))]
(go (while (< 0 (count @a-chans))
(println "iteration started : " (vec @a-chans))
(let [[v ch] (alts! (vec @a-chans))]
(if v
(f v ch)
(swap! a-chans #(disj % ch))))))))
函数f在alts的结果上执行!渠道是开放的。我在这里保留了一组带有开放通道的原子。一旦我找到一个已关闭的频道,我将其从此集中删除。如果没有更多打开的频道,则while循环停止。你可以运行它:
(def c1 (chan))
(def c2 (chan))
(def c3 (chan))
(alts-while-open (fn [v ch] (println v)) c1 c2 c3)
现在当某些内容被写入任何这些频道时,它就会被打印出来。您可以看到新的迭代在此之后开始。关闭通道后,您可以看到迭代开始但通道向量减少了。关闭所有通道后,while循环停止。
很难回答是否使用关闭的问题!函数或其他一些通知机制来停止循环。我认为这取决于具体情况。如果没有任何复杂的处理和#34;通信终止&#34;我会关闭!这个频道。如果有更复杂的逻辑 - 例如有成功的沟通结束&#34;并且失败&#34;沟通结束&#34;选项,我想以不同的方式处理它们,然后我宁愿发送一条特殊的信息。像
这样的东西[:end-of-communication :success]
答案 1 :(得分:0)
当您不发送简单值时,发送特殊end-of-communication
的想法不起作用,因为您无法保证按照您的顺序放置和接受值打算让他们拥有。
下一个想法是发送者和接收者都事先知道要处理的值的数量,例如像这样:
user> (<!!
(let [c1 (chan)
values ["hi" "there"]
vcount (count values)]
(doseq [value values]
(thread
(>!! c1 value)))
(thread
(loop [recvalue (<!! c1)
reccount 1]
(println "Read" recvalue)
(if (= reccount vcount)
(do (close! c1)
:done)
(recur (<!! c1) (inc reccount)))))))
Read hi
Read there
:done
这原则上有效,但有明显的缺点,你必须在设置发送和接收过程之前就金额达成一致意见,以及不明显的缺点,即如果在发送方出现问题,你可以&#39; ll最终会在接收端无休止地等待(假设发送方做的不仅仅是将值放到通道上)。
我得出的结论是,使这个可靠的唯一方法是使用timeout
频道,如下所示:
(<!! (let [c1 (chan)
tchan (timeout 1000)
values ["hi" "there"]]
(doseq [value values]
(thread
(>!! c1 value)))
(thread
(loop [[recvalue rchan] (alts!! [c1 tchan])
timeoutchan tchan]
(if (= rchan timeoutchan)
(do (close! c1)
:done)
(do (println "Read" recvalue)
(let [newtimeout (timeout 1000)]
(recur (alts!! [c1 newtimeout])
newtimeout))))))
Read hi
Read there
:done