3

我很好奇是否有人提出了测试多线程应用程序的好策略。

我用 midje 做了很多测试,这对于测试功能非常有用......但我不确定如何测试多线程代码而不让它看起来真的很hacky:

  (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))

并远离睡眠。有没有策略来做到这一点?

4

2 回答 2

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 毫秒内满足,这将立即返回 true,如果不满足some-check条件,则在 1000 毫秒后返回 false。

于 2012-12-05T06:24:42.173 回答
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
于 2013-04-16T23:41:27.843 回答