Core.async <! - 通道死锁

时间:2015-11-20 16:55:08

标签: clojure clojurescript core.async

为什么Alpha会提前停止,当我希望它的行为像Beta一样? Alpha和Beta之间的唯一区别是>!put!,如下所示。

α

user=> (def q (chan))
#'user/q
user=> (def counter (atom 0))
#'user/counter
user=> (defn mg [event-queue]
  #_=>      (go-loop [event (<! event-queue)]
  #_=>       (swap! counter inc)
  #_=>       (when (< @counter 4)
  #_=>         (println "counter: " @counter)
  #_=>         (>! event-queue {:a @counter})      ;; Here's the only difference
  #_=>         (println "event: " event)
  #_=>         (recur (<! event-queue)))))
#'user/mg
user=> (mg q)
#object[clojure.core.async.impl.channels.ManyToManyChannel 0x3a1ffd56 "clojure.core.async.impl.channels.ManyToManyChannel@3a1ffd56"]
user=> (put! q "hi")
counter:  true
1
user=>

β

user=> (def q (chan))
#'user/q
user=> (def counter (atom 0))
#'user/counter
user=> (defn mg [event-queue]
  #_=>   (go-loop [event (<! event-queue)]
  #_=>    (swap! counter inc)
  #_=>    (when (< @counter 4)
  #_=>      (println "counter: " @counter)
  #_=>      (put! event-queue {:a @counter})      ;; Here's the only difference
  #_=>      (println "event: " event)
  #_=>      (recur (<! event-queue)))))
#'user/mg
user=> (mg q)
#object[clojure.core.async.impl.channels.ManyToManyChannel 0x72c9b65a "clojure.core.async.impl.channels.ManyToManyChannel@72c9b65a"]
user=> (put! q "hi")
true
counter:  1
event:  hi
counter:  2
event:  {:a 1}
counter:  3
event:  {:a 2}
user=> 

有趣的是,在执行Alpha之后,频道#'user/q已正确排队:

user=> (take! q println)
event:  hi
{:a 1}
nil
user=> 

在Clojure和Clojurescript中都会出现相同的结果。这是某种僵局,还是假设发生?

1 个答案:

答案 0 :(得分:4)

这是预期的。

创建通道q时没有缓冲区,因此当使用>!放置一个值时,它将阻塞(停放)go-loop,直到另一个线程准备好使用该值与<!

解决此问题的一种方法是为q提供带有(def q (chan 1))的1插槽缓冲区。缓冲区允许将1个值放在通道中,而不会阻塞发送方。

Beta表现不同,因为put!是异步的。调用者 - 它使用单独的线程将新值放入通道中。这样可以避免阻塞当前go-loop,从而允许读取通道并继续进行。