如何启动两个线程并等待它们

时间:2011-12-27 18:48:34

标签: clojure

我可以启动两个线程但它们可以同步工作。为了让这些线程独立发布,我缺少什么?

主要,线程和输出

(defn -main 
    [& args]
    (do
        (let [grid-dim-in [0 5]
              mr1-pos     [\N 2 4]
              mr2-pos     [\N 1 5]
              mr1-movs    "LMLMMRMM"
              mr2-movs    "RMRMMMLM"]

            (reset! grid-dim grid-dim-in)
            (reset! mr1-id {:mr1 mr1-pos})
            (reset! mr2-id {:mr2 mr2-pos})

            (.start (Thread. (rover-thread mr1-id mr1-movs update-work-block)))
            (.start (Thread. (rover-thread mr2-id mr2-movs update-work-block))))))

(defn rover-thread [id movs update-ref]
    (let [id-key (keys @id)
          id-vals (vals @id)]
        (doseq [mov movs]
           (println "Rover " id-key " is moving ")
           (let [new-mov (determine-rover-move (first id-vals) mov)]
               (move-rover id new-mov update-ref)
               (print "Rover ")
               (print (first id-key))
               (print " is at ")
               (println new-mov)
               (Thread/sleep (rand 1000)))))

Rover :mr1 is at [E 2 4]
Rover  (:mr1)  is moving 
Rover :mr1 is at [N 2 5]
Rover  (:mr1)  is moving 
Rover :mr1 is at [N 2 5]
Finished on Thread[main,5,main]
Rover  (:mr2)  is moving 
Rover :mr2 is at [E 1 5]
Rover  (:mr2)  is moving 
Rover :mr2 is at [N 1 6]

2 个答案:

答案 0 :(得分:7)

仔细研究这两行:

(.start (Thread. (rover-thread mr1-id mr1-movs update-work-block)))
(.start (Thread. (rover-thread mr2-id mr2-movs update-work-block))))))

此代码首先评估(rover-thread mr1-id mr1-movs update-work-block),并将结果传递给Thread的构造函数,这不是您想要的。

这是一个简单的函数来说明原理。这不起作用,因为在(f ...)传递给Thread构造函数的结果之前会对其进行求值:

(defn run-thread-thing-wrong []
  (let [f (fn [n s]
            (doseq [i (range n)]
              (prn s i)
              (Thread/sleep (rand 1000))))]
    (.start (Thread. (f 10 "A")))
    (.start (Thread. (f 10 "B"))))
  nil)

这是一个有效的版本。 函数将传递给Thread构造函数:

(defn run-thread-thing []
  (let [f (fn [n s]
            (doseq [i (range n)]
              (prn s i)
              (Thread/sleep (rand 1000))))]
    (.start (Thread. (fn [] (f 10 "A"))))
    (.start (Thread. (fn [] (f 10 "B")))))
  nil)

注意:您可以使用短格式(fn [] ....)代替#(....)匿名函数。

这是另一个版本相同的版本,但使用future而不是手动创建线程:

(defn run-thread-thing []
  (let [f (fn [n s]
            (doseq [i (range n)]
              (prn s i)
              (Thread/sleep (rand 1000))))]
    (future (f 10 "A"))
    (future (f 10 "B")))
  nil)

请注意,在这种情况下,您将表单传递给future而不是函数。

答案 1 :(得分:1)

这似乎是一个使用Clojure agent feature的好地方。我没有资格完全解释如何使用它们,但可以找到一个非常好的使用示例here。使用代理启动线程非常简单,我认为它更具惯用性。

代码看起来像,

(def rover1 (agent [mr1-posn mr1-movs mr1-id])) 
(def rover2 (agent [mr2-posn mr2-movs mr2-id])) 
(defn rover-behave [[posn movs id]]
  (send-off *agent* #'rover-behave)
  (. Thread (sleep 1000))
  (let [new-mov (determine-rover-move posn movs id)
        new-posn (posn-after-move posn new-mov)]
    ;return value updates state of agent
    [new-posn movs id]
  )
)
(send-off rover1 rover-behave)
(send-off rover2 rover-behave)