如何在放入-chan之前防止关闭!

时间:2016-05-06 13:39:39

标签: asynchronous clojure core.async transducer

我想运行像

这样的代码
(->> input
     (partition-all 5)
     (map a-side-effect)
     dorun)

异步分割输入和输出(a-side-effect)。

然后我编写了以下实验代码。

;; using boot-clj
(set-env! :dependencies '[[org.clojure/core.async "0.2.374"]])
(require '[clojure.core.async :as async :refer [<! <!! >! >!!]])

(let [input (range 18)
      c (async/chan 1 (comp (partition-all 5)
                            (map prn)))]
  (async/onto-chan c input false)
  (async/close! c))

此代码的解释:

  • 实际上,输入中的元素及其数量在运行前未定义,输入中的元素可以由0到10之间的某些数字取得。
  • async/onto-chan用于将Seq元素(输入片段)放入通道c并将被多次调用,因此第三个参数为false
  • prn可替代a-side-effect

我预计上面的代码会打印

[0 1 2 3 4]
[5 6 7 8 9]
[10 11 12 13 14]
[15 16 17]

在REPL中但它不打印任何字符。

然后我加时间等待,就像这样

(let [c (async/chan 1 (comp (partition-all 5)
                            (map prn)))]
  (async/onto-chan c (range 18) false)
  (Thread/sleep 1000) ;wait
  (async/close! c))

这段代码给出了我的预期输出。

然后我检查core.async/onto-chan

我认为发生了什么:

  1. 我的代码中c频道已core.async/close!
  2. core.async/onto-chan的参数中的每一项都被core.async/>!放在go-loop onto-chan中,因为频道c已关闭。< / LI>

    有确定的方法可以在close!之前放置项目吗? 写onto-chan的同步版本而不使用go-loop

    或者我的想法是错的?

2 个答案:

答案 0 :(得分:1)

你的第二个例子Thread.sleep只有'错误地运作'。

它起作用的原因是来自c的传感器的每个转换结果值都是nil,并且因为通道中不允许nil s,所以会抛出异常,并没有将值放入c:这是允许生产者onto-chan继续进入通道而不是阻止等待的原因。如果将第二个示例粘贴到REPL中,您将看到四个堆栈跟踪 - 每个分区一个。

nil当然是由于映射prn,这是一个副作用函数,为所有输入返回nil

如果我理解你的设计,你的目标就是做这样的事情:

(defn go-run! [ch proc]
  (async/go-loop []
    (when-let [value (<! ch)]
      (proc value)
      (recur))))

(let [input (range 18)
      c (async/chan 1 (partition-all 5))]
  (async/onto-chan c input)
  (<!! (go-run! c prn)))
  • 你真的需要一个制作人和一个消费者,否则你的程序会阻止。我介绍了一个go-loop消费者。
  • 一般来说,map和副作用不能很好地结合在一起,所以我将副作用prn提取到消费者中。
  • onto-chan不能多次调用(至少在显示的代码中),因此它不需要false参数。

答案 1 :(得分:0)

采取megakorre的想法:

(let [c (async/chan 1 (comp (partition-all 5)
                            (map prn)))
      put-ch (async/onto-chan c (range 18) false)]
  (async/alts!! [put-ch])
  (async/close! c))