Clojure async / go如何停止阻塞代码

时间:2017-10-20 12:27:46

标签: multithreading concurrency clojure core.async

我使用一些Java库来进行非异步获取和发布请求。我曾经将这些请求包装到期货中,它解决了“等待问题”(我的意思是等待回复)

(defn unchangeable-lib-request [n]
  (Thread/sleep 1000)
  n)

(defn process [n]
  (let [res (atom [])]
    (dotimes [i n]
      (future (swap! res conj (unchangeable-lib-request i))))
    (loop []
      (if (> n (count @res))
        (recur)
        @res))))

(time (process 9))

;; "Elapsed time: 1000.639079 msecs"
;; => [8 7 5 6 4 3 2 1 0]

但我需要创建数百个请求,这会产生性能问题。我发现了core.async和go块。但是如果我在这个库中使用go-blocks,它将无法解决“等待问题”

(defn unchangeable-lib-request [n]
  (Thread/sleep 1000)
  n)

(defn process [n]
  (let [c (async/chan 10)]
    (dotimes [i n]
      (async/go
        (async/>! c (unchangeable-lib-request i))))
  (loop [result []]
    (if (> n (count result))
      (recur (conj result (async/<!! c)))
      result))))

(time (process 9))

;; "Elapsed time: 2001.770183 msecs"
;; => [0 4 1 6 7 2 5 3 8]

Go块可以同时处理8个请求。是否有可能编写一些async-wrapper来停止go-block并提供异步生成100个请求而不会相互阻塞的能力?

(defn process [n]
  (let [c (async/chan 10)]
    (dotimes [i n]
      (async/go
        (async/>! c (magic-async-parking-wrapper
                      (unchangeable-lib-request i))))
  (loop [result []]
    (if (> n (count result))
      (recur (conj result (async/<!! c)))
      result))))

(time (process 9))

;; "Elapsed time: 1003.2563 msecs"

我知道异步/线程,但似乎这与(future ...)相同。

有可能吗?

2 个答案:

答案 0 :(得分:2)

我建议:

  • 使用期货来创建线程,让他们使用put!将结果重新放回任何go块之外的核心异步通道,例如:(future (put! chan (worker-function)))
  • 然后使用go块在该(单个)频道上等待,在获得结果时输入结果。

答案 1 :(得分:1)

这是您使用clojure.core.async/pipeline-blocking

的地方
(require '[clojure.core.async :as a :refer [chan pipeline-blocking]])


(let [output-chan (chan 100)
      input-chan (chan 1000)]
  (pipeline-blocking 4 ; parallelism knob
                     output-chan 
                     (map unchangeable-lib-request) 
                     input-chan)
  ;; Consume results from output-chan, put operations on input-chan
  [output-chan input-chan]
  )

这会产生n(在这种情况下为4)线程,这些线程一直忙于执行unchangeable-lib-request

使用output-chan的缓冲区大小来提前调整您想要发生的请求数量。

使用input-chan的缓冲区大小来微调您希望在没有反向传播的情况下安排的请求数量(阻止input-chan)。