什么是最好的方式!在多个通道的矢量?

时间:2015-03-10 03:12:21

标签: clojure core.async

我使用core.async并行执行某些操作,然后使用alts!!等待一定量的结果并超时。

(ns c
  (:require [clojure.core.async :as a]))

(defn async-call-on-vector [v]
  (mapv (fn [n]
          (a/go (a/<! (a/timeout n)) ; simulate long time work
                n))
        v))

(defn wait-result-with-timeout [chans num-to-get timeout]
  (let [chans-count (count chans)
        num-to-get (min num-to-get
                        chans-count)]
    (if (empty? chans)
      []
      (let [timeout (a/timeout timeout)]
        (loop [result []
               met 0]
          (if (or (= (count result) num-to-get)
                  (= met chans-count)) ; all chan has been consumed
            result
            (let [[v c] (a/alts!! (conj chans timeout))]
              (if (= c timeout)
                result
                (case v
                  nil (do (println "got nil") (recur result met)) ; close! on that channel
                  (recur (conj result v) (inc met)))))))))))

然后调用:

user=> (-> [1 200 300 400 500] c/async-call-on-vector (c/wait-result-with-timeout 2 30))

这个表达式会输出很多got nil。似乎go块返回的通道将在返回结果后关闭该通道。这将导致alts!!在这种情况下返回nil。但这对CPU来说非常不友好,就像忙着等待。有没有办法避免这种情况?

我通过定义像go这样的宏来解决这个问题,但是返回一个在返回结果时不会关闭的通道。这是解决问题的正确方法吗?

1 个答案:

答案 0 :(得分:1)

  

我使用core.async并行执行某些操作,然后使用alts !!等待超时的一定数量的结果。

看起来您希望收集某些频道将传递的所有值,直到所有这些频道都关闭,或者直到发生超时。一种方法是将merge个频道放到一个频道上,然后在alts!中使用go-loop将值集合到一个向量中:

(defn wait-result-with-timeout [chans timeout]
  (let [all-chans (a/merge chans)
        t-out (a/timeout timeout)]
    (a/go-loop [vs []]
      (let [[v _] (a/alts! [all-chans t-out])]
        ;; v will be nil if either every channel in
        ;; `chans` is closed, or if `t-out` fires.
        (if (nil? v)
          vs
          (recur (conj vs v)))))))
  

看来,go块返回的通道会在返回结果后关闭该通道。

你是对的,这是go块的记录行为。

  

我通过定义像go这样的宏来解决这个问题,但是返回一个在返回结果时不会关闭的通道。这是解决问题的正确方法吗?

可能不是,虽然我不能说你的特定用例是对还是错。一般来说,如果渠道完成交付价值,渠道应该关闭,以表明完成交付价值的语义。例如,上面的代码使用all-chans的结束来表示没有其他工作要等待。