我使用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这样的宏来解决这个问题,但是返回一个在返回结果时不会关闭的通道。这是解决问题的正确方法吗?
答案 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
的结束来表示没有其他工作要等待。