Core.async <! 渠道僵局

Nic*_*ick 2 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=>
Run Code Online (Sandbox Code Playgroud)

测试版:

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=> 
Run Code Online (Sandbox Code Playgroud)

同样有趣的是,执行 Alpha 后,通道#'user/q已正确排队:

user=> (take! q println)
event:  hi
{:a 1}
nil
user=> 
Run Code Online (Sandbox Code Playgroud)

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

lnm*_*nmx 5

这是预料之中的。

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

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

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