Ich frage mich, warum die folgenden 2 Anrufungen unterschiedlich auf verhalten sich je nachdem, ob die ensure
Funktion innerhalb eingeführt wird oder außerhalb let
:"Sicherstellen" -Funktion in Clojure funktioniert in `Let` falsch?
=> "inside let"
(def account (ref 1000))
(def secured (ref false))
(def started (promise))
=> #'user/account
=> #'user/secured
=> #'user/started
(defn withdraw [account amount secured]
(dosync
(let [secured-value (ensure secured)]
(deliver started true)
(Thread/sleep 5000)
(println :started)
(when-not secured-value
(alter account - amount))
(println :finished))))
=> #'user/withdraw
(future (withdraw account 500 secured))
@started
(dosync (ref-set secured true))
=> #<[email protected]: :pending>
=> true
:started
:finished
=> true
@account
=> 500
========
=> "outside let"
(def account (ref 1000))
(def secured (ref false))
(def started (promise))
=> #'user/account
=> #'user/secured
=> #'user/started
(defn withdraw [account amount secured]
(dosync
(let [secured-value @secured]
(deliver started true)
(Thread/sleep 5000)
(println :started)
(when-not (ensure secured)
(alter account - amount))
(println :finished))))
=> #'user/withdraw
(future (withdraw account 500 secured))
@started
(dosync (ref-set secured true))
=> #<[email protected]: :pending>
=> true
=> true
:started
:started
:finished
@account
=> 1000
Die erwartete Semantik hier ist, dass, wenn secured
auf true
gesetzt ist, sollte man nicht in der Lage sein, Geld abzuziehen.
Mein Verständnis ist, dass ensure
Funktion wird sicherstellen, dass die secured
ref nicht während der Zeitspanne der Transaktion geändert hat, so scheint das zweite Verhalten mit Transaktion Neustart sinnvoll, aber warum verhält es sich anders im ersten Fall?
Update: versuchten ohne Tread/sleep:
(def account (ref 1000))
(def secured (ref false))
(def started (promise))
=> #'user/account
=> #'user/secured
=> #'user/started
(defn withdraw [account amount secured]
(dosync
(let [secured-value (ensure secured)]
(deliver started true)
;(Thread/sleep 5000)
(println :started)
(when-not secured-value
(alter account - amount))
(println :finished))))
=> #'user/withdraw
@account
=> 1000
(future (withdraw account 500 secured))
@started
(dosync (ref-set secured true))
=> #<[email protected]: :pending>
:started
:finished
=> true
=> true
@account
=> 500
Mit einem bisschen mehr experimentellen Testen eines ref-set
(def account (ref 1000))
(def secured (ref false))
(def started (promise))
=> #'user/account
=> #'user/secured
=> #'user/started
(defn withdraw [account amount secured]
(dosync
(let [secured-value (ensure secured)]
(deliver started true)
(Thread/sleep 5000)
(println :started)
(when-not secured-value
(alter account - amount))
(println :finished))))
=> #'user/withdraw
(future (withdraw account 500 secured))
@started
(dosync do ((println "change started") (ref-set secured true) (println "change done.")))
=> #<[email protected]: :pending>
=> true
change started
...
change started
change started
:started
:finished
change done.
NullPointerException user/eval2176/fn--2177 (form-init3061788549693294520.clj:3)
@account
=> 500
Nachdem ich Ihre zweite Antwort noch einmal gelesen habe, habe ich ein wenig mit dem 'ref-set 'experimentiert (Ergebnisse werden zu der Frage hinzugefügt - nicht sicher *** was NPE verursacht, ***), sieht aus wie' sicherstellen ' blockiert in der Tat den Schreibvorgang, bis der "Zurückziehen" -Tx beendet ist. Also zu meiner Frage früher: "Bedeutet dieser *** Schutz ***, dass er irgendwelche Änderungen ignoriert, die außerhalb des aktuellen Senders an dem Ref vorgenommen wurden, oder bedeutet dies, dass er nach irgendwelchen externen Änderungen des Refs Ausschau hält und den aktuellen Tx neu startet wenn sie auftreten? " Du hast gesagt, dass das "Letztere" richtig ist, also sagst du, dass du erkannt hast, dass das erstgenannte Recht haben würde. Richtig? –
Die NPE ist, weil Sie ein '(' an der falschen Stelle in der aktualisierten Ref-Set-Code. Es sollte vor dem 'tun' sein. – DaoWen
Auch scheint das Verhalten im Gegensatz zu' sichern''s Dokumentation: " Erlaubt mehr Parallelität als (Ref-set ref @ref) "- Ich bin mir nicht sicher, wie all diese Sperren mehr Concurrency ermöglichen. Klingt anders als das, was Rich hier erklärt: [https://groups.google.com/forum/ #! topic/clojure/vxlzONoaba4]. –