测试 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
中的阻塞 <!!
将几乎立即具有一个值,并且 go
从 process-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
块时 put
在 chan-test
中提供一个值] 终止。但我认为,考虑到您展示的代码,其他方法可能会不必要地更加复杂。
在 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
中的阻塞 <!!
将几乎立即具有一个值,并且 go
从 process-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
块时 put
在 chan-test
中提供一个值] 终止。但我认为,考虑到您展示的代码,其他方法可能会不必要地更加复杂。