【问题标题】:How to prevent close!-ing before put-ing in onto-chan如何防止关闭!-ing 在放入 on-chan 之前
【发布时间】:2016-05-06 13:39:38
【问题描述】:

我想运行类似的代码

(->> input
     (partition-all 5)
     (map a-side-effect)
     dorun)

异步划分输入和输出(副作用)。

然后我编写了下面的代码进行实验。

;; 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
  • prna-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!ed。
  2. core.async/onto-chan 参数的每一项都被放在onto-chan 中的go-loop 中(core.async/&gt;!)徒劳无功,因为通道c 已关闭。

有没有办法将项目放在close!ing 之前? 写一个同步版本的onto-chan 不使用go-loop

还是我的想法错了?

【问题讨论】:

  • 你能解释一下你的目标是什么吗?为什么要创建 chanc,将一个传感器与它关联,但从不从中获取任何值?
  • clojure.github.io/core.async/#clojure.core.async/onto-chan on-chan 函数返回一个你可以等待然后关闭c的频道。
  • @gits - 我的目标太复杂,无法在这里解释。我刚刚写了我的生产代码的简洁版本。当然,如果只是打印数字,还有很多方法。正如您所说,chan c 不是标记任何值,而是使用 prn 本身的值。
  • @megakorre - 谢谢。问题似乎解决了。

标签: asynchronous clojure core.async transducer


【解决方案1】:

您使用Thread.sleep 的第二个示例只是错误地“起作用”。

它起作用的原因是,从c的转换器出来的每个转换结果值都是nil,并且由于通道中不允许nils,因此抛出异常,并且不放置任何值进入c:这是允许生产者onto-chan 继续放入频道而不阻塞等待的原因。如果您将第二个示例粘贴到 REPL 中,您将看到四个堆栈跟踪 - 每个分区一个。

nils 当然是由于映射到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 参数。

【讨论】:

  • 谢谢,我大概明白了。但我还是有一些我无法理解的东西。代码最后一行的&lt;!! 是什么意思?防止go-run!的线程被JVM终止消费未完成?
  • 是的,记住这里的代码是在主线程中运行的,但是所有分派到go设施的东西都是异步的并且在守护线程中运行,所以你必须包括一些与@987654339同步的方法@线程的完成。这里&lt;!! 阻塞了主线程(作为最后一个操作),直到go-run! 返回的通道发出完成信号。
【解决方案2】:

采用 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))

【讨论】:

  • 我仍然对您要解决的问题感到困惑。当我将此代码粘贴到我的 REPL 中时,我收到一个断言错误:您的传感器映射到 prn,它为每个值返回 nil,并且无法将 nils 放入通道或从通道中取出。
  • @glts - 抱歉我的解释不佳。我想异步运行(-&gt;&gt; (range 18) (partition-all 5) (map prn) dorun)prn 是我真正想做的副作用的替代品。
  • 我没有注意到nils 不能被放入或从频道中取出。但是在我的环境(clojure 1.7.0 和 1.8.0)中没有发生断言错误。正如我所写的那样,与返回 nil 的chan 相关的传感器可能无论如何都不好。
  • 如您在终端仿真器中所说,发生了异常,但带有苹果酒的 emacs 没有。也许这就是你我的不同。
  • 无论你看到与否,都会发生异常。在 Vim 中,异常也不可见(因为它发生在不同的线程中),但它仍然在后台抛出。
猜你喜欢
  • 1970-01-01
  • 2018-05-11
  • 1970-01-01
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
相关资源
最近更新 更多