2016-07-25 11 views
2

Ich gehe durch das Buch 7 concurrency models in 7 weeks. Darin Philosophen als eine Anzahl von ref ‚s vertreten:Understanding STM Eigenschaften in Clojure

(def philosophers (into [] (repeatedly 5 #(ref :thinking)))) 

Der Zustand jeden Philosophen zwischen :thinking und :eating mit dosync Transaktionen gewährleisten Konsistenz gekippt wird.

Jetzt mag ich einen Thread haben, die aktuellen Status ausgibt, so dass ich sicher sein kann, dass der Staat zu allen Zeiten gültig ist:

(defn status-thread [] 
    (Thread. 
    #(while true 
     (dosync 
     (println (map (fn [p] @p) philosophers)) 
     (Thread/sleep 100))))) 

Wir mehr verwenden @ Werte jeden Philosophen zu lesen. Es kann passieren, dass einige Referenzen geändert werden, wie wir über Philosophen map. Würde es uns veranlassen, inkonsistenten Zustand zu drucken, obwohl wir keinen haben?

Ich bin mir bewusst, dass Clojure verwendet MVCC zu implementieren STM, aber ich bin mir nicht sicher, ob ich es richtig anwenden.

Meine Transaktion enthält Nebenwirkungen und sollte in der Regel nicht innerhalb einer Transaktion angezeigt werden. Aber in diesem Fall wird die Transaktion immer erfolgreich sein und die Nebenwirkung sollte nur einmal stattfinden. Ist das akzeptabel?

Antwort

1

Ihre Transaktion benötigt nicht wirklich einen Nebeneffekt, und wenn Sie das Problem so hoch skalieren, glaube ich, dass die Transaktion fehlschlagen kann, wenn Geschichte fehlt, und den Nebeneffekt wiederholen, wenn viel geschrieben wird. Ich denke, der geeignetere Weg wäre hier, die dosync näher heranzuziehen. Die Transaktion sollte eine reine, nebenwirkungsfreie Fact Finding Mission sein. Sobald dies zu einem Wert geführt hat, können Sie dann Nebenwirkungen ausführen, ohne das STM zu beeinträchtigen.

(defn status-thread [] 
    (-> #(while true 
     (println (dosync (mapv deref philosophers))) 
     (Thread/sleep 100)) 
    Thread. 
    .start)) ;;Threw in starting of the thread for my own testing 

Ein paar Dinge, die ich hier erwähnen möchte:

  1. @ ist ein Leser Makro für die deref fn, so (fn [p] @p) nur deref entspricht.
  2. Sie sollten Faulheit in Transaktionen vermeiden, da einige der faulen Werte außerhalb des Kontextes der dosync oder gar nicht ausgewertet werden können. Für map Pings bedeutet das, dass Sie z.B. doall, oder wie hier nur die eifrig ausgewertete mapv Variante, die einen Vektor und nicht eine Sequenz macht.
0

Diese Kontingenz wurde in das STM-Design aufgenommen.

Dieses Problem wird explizit durch die Kombination von Agenten mit Refs gelöst. Refs garantieren, dass alle Nachrichten, die an Agenten in einer Transaktion eingestellt sind, genau einmal gesendet werden und sie werden nur gesendet, wenn die Transaktion festgeschrieben wird. Wenn die Transaktion wiederholt wird, werden sie gelöscht und nicht gesendet. Wenn die Transaktion schließlich durchkommt, werden sie zum Zeitpunkt der Transaktion gesendet.

(def watcher (agent nil)) 

(defn status-thread [] 
    (future 
    (while true 
    (dosync 
     (send watcher (fn [_] (println (map (fn [p] @p) philosophers)))) 
     (Thread/sleep 100))))) 

Die STM garantiert, dass Ihre Transaktion nicht verpflichtet werden, wenn die Schiedsrichter bei der Transaktion deref, wo Änderungen in einer inkompatiblen Weise, während er ausgeführt wurde.Sie müssen sich nicht explizit darum kümmern, dass mehrere Refs in einer Transaktion deaktiviert werden (für das der STM erstellt wurde)