测试 chan 处理函数时结果不一致

Inconsistent results when testing a chan processing function

midje 框架内测试的 process-async 函数产生不一致的结果。大多数时候它会按预期进行检查,但有时会在其初始状态 ("") 读取 out.json。在检查之前,我依靠 async-blocker 函数等待 process-async

我的方法有什么问题?

(require '[[test-with-files.core :refer [with-files public-dir]])

(defn async-blocker [fun & args]
  (let [chan-test (chan)]
    (go (>! chan-test (apply fun args)))
    (<!! chan-test)))

(defn process-async
  [channel func]
  (go-loop  []
    (when-let  [response  (<! channel)]
      (func response)
      (recur))))

(with-files [["/out.json" ""]]
    (facts "About `process-async"
           (let [channel (chan)
                 file (io/resource (str public-dir "/out.json"))
                 write #(spit file (str % "\n") :append true)]
             (doseq [m ["m1" "m2"]] (>!! channel m))
             (async-blocker process-async channel write)
             (clojure.string/split-lines (slurp file)) => (just ["m1" "m2"] :in-any-order)
             )
           )
    )

问题是 process-async return 立即与 "[...] 一个通道,当 已完成(因为 go-loop 只是 (go (loop ...))go return 的语法糖。

这意味着 async-blocker 中的阻塞 <!! 将几乎立即具有一个值,并且 goprocess-async 和 [=16= 阻塞的顺序] 是否被处决未定。可能大多数时候来自 process-async 的块首先执行,因为它首先被创建,但这在并发上下文中并不能保证。

根据 <!! 的文档 "Will return nil if closed. Will block if nothing is available." 这意味着如果您可以假设 return 的值为 (apply fun args) 是 return 由 go 编辑的频道,您应该可以按以下方式使用 <!! 进行阻止:

(defn async-blocker [fun & args]
  (<!! (apply fun args)))

一旦通道中有值(即来自 go 块的 return 值),这将解除阻塞。

还有其他选项可以等待另一个go块的结果。例如,您可以提供原始 chan-test 作为 fun 的参数,然后在 [=28= 中创建 go 块时 putchan-test 中提供一个值] 终止。但我认为,考虑到您展示的代码,其他方法可能会不必要地更加复杂。