我很好奇是否有人提出了测试多线程应用程序的好策略。
我使用midje进行了很多测试,这对于测试函数非常有用......但是我不确定如何测试多线程代码而不看起来真的很糟糕:
(fact "the state is modified by a thread call"
(Thread/sleep 100)
(check-state-eq *state* nil)
(Thread/sleep 100)
(modify-state-thread-call *state* :updated-value)
(Thread/sleep 100)
(check-state-eq *state* :updated-value))
有时,由于编译时间的原因,我的测试失败了,因为状态没有及时更新,所以我必须睡得更久。理想情况下,我想要一种方法来编写类似的东西:
(fact "the state is modified by a thread call"
(modify-state-thread-call *state* :updated-value)
=leads-to=> (check-state-eq *state* :updated-value))
远离睡眠。是否有策略来做到这一点?
答案 0 :(得分:5)
如果此示例中的*state*
是其中一种clojure引用类型,则可以使用add-watch添加一个通知该对象的每次更改的函数:http://clojuredocs.org/clojure_core/clojure.core/add-watch
我建议的方法是在条件满足时使用手表来表达承诺。
(let [check-promise (promise)]
(add-watch *state* :check-for-updated-value
(fn [rkey refr _oldval newval]
(when (some-check newval)
(remove-watch refr rkey)
(deliver check-promise true))))
(modify-state-thread-call *state* :updated-value)
(deref check-promise 1000 false))
如果*state*
在1000毫秒内接受满足some-check
的值,或者如果条件不满足则在1000毫秒后返回false,则会立即返回true。
答案 1 :(得分:1)
根据Crate的回复,我创建了一个wait
函数:
(defn return-val [p ms ret]
(cond (nil? ms) (deref p)
:else (deref p ms ret)))
(defn wait
([f rf] (wait f rf nil nil))
([f rf ms] (wait f rf ms nil))
([f rf ms ret]
(let [p (promise)
pk (hash-keyword p)
d-fn (fn [_ rf _ _]
(remove-watch rf pk)
(deliver p rf))]
(add-watch rf pk d-fn)
(f rf)
(return-val p ms ret))))
它的用法是:
(defn threaded-inc [rf]
(future
(Thread/sleep 100)
(dosync (alter rf inc)))
rf)
(def arf (ref 0))
(deref (threaded-inc arf)) ;=> 0
(dosync (ref-set arf 0))
(deref (wait threaded-inc arf)) ;=> 1