在条件成立之前,如何阻止线程?

时间:2014-04-16 03:41:50

标签: concurrency clojure

在Clojure中,在条件成立之前,如何阻止线程(未来)?或者,或许,可能会继续重试,直到条件成立为止?当你有条件变量时这很容易,但我不确定Clojure的方法是什么。

更具体地说,我有一个共享变量,可以同时被许多期货访问。未来应该做到以下几点:

  1. 检查变量的状态。
  2. 如果状态符合某个条件,请将其更新为新状态。
  3. 如果状态不符合条件,则未来应该阻止或重试,直到满足条件(通过另一个修改状态的线程)。

3 个答案:

答案 0 :(得分:1)

Java平台支持条件变量,请参阅java.util.concurrent.locks.Condition的文档。

上页中的示例很容易转换为Clojure:

;;; based on the example in java.util.concurrent.locks.Condition
;;; documentation for JDK 1.7, see the link above

(defprotocol PBoundedBuffer
  (-put [buf x])
  (-take [buf]))

(import (java.util.concurrent.locks ReentrantLock Condition))

(deftype BoundedBuffer [^ReentrantLock lock
                        ^Condition not-full?
                        ^Condition not-empty?
                        ^objects items
                        ^:unsynchronized-mutable ^int putptr
                        ^:unsynchronized-mutable ^int takeptr
                        ^:unsynchronized-mutable ^int cnt]
  PBoundedBuffer
  (-put [buf x]
    (.lock lock)
    (try
      (while (== cnt (alength items))
        (.await not-full?))
      (aset items putptr x)
      (set! putptr (unchecked-inc-int putptr))
      (if (== putptr (alength items))
        (set! putptr (int 0)))
      (set! cnt (unchecked-inc-int cnt))
      (.signal not-empty?)
      (finally
        (.unlock lock))))

  (-take [buf]
    (.lock lock)
    (try
      (while (zero? cnt)
        (.await not-empty?))
      (let [x (aget items takeptr)]
        (set! takeptr (unchecked-inc-int takeptr))
        (if (== takeptr (alength items))
          (set! takeptr (int 0)))
        (set! cnt (unchecked-dec-int cnt))
        (.signal not-full?)
        x)
      (finally
        (.unlock lock)))))

(defn bounded-buffer [capacity]
  (let [lock (java.util.concurrent.locks.ReentrantLock.)]
    (BoundedBuffer. lock
                    (.newCondition lock)
                    (.newCondition lock)
                    (object-array capacity)
                    0
                    0
                    0)))

REPL的试驾:

(def bb (bounded-buffer 3))

(-put bb 1)
(-put bb 2)
(-put bb 3)

(future (-put bb 4) (println :foo))

(-take bb)

根据需要,未来阻止,然后在最后一次调用:foo后打印-take

答案 1 :(得分:0)

Clojure为此类问题提供refsagentsatoms,如果您的问题没有任何副作用,听起来您可以使用参考号。

答案 2 :(得分:0)

您可以存储您在代理中检查的条件或值,并添加一个在条件变为true时执行所需操作的手表,或者值是您想要的值:

(def x (agent 0))

(defn do-this-once-x-is-10 [key agnt old-val new-val]
  (when (= new-val 10)
    (println "x is now 10")))

(add-watch x :print-alert do-this-once-x-is-10)

(dotimes [_ 10]
  (Thread/sleep 1000)
  (send x inc))

; the value stored in x is incremented every second; after 10 seconds,
; the value of x will equal 10 and "x is now 10" will print