2017-02-26 2 views
0

Die process-async-Funktion, die innerhalb des midje-Frameworks getestet wurde, führt zu inkonsistenten Ergebnissen. Die meiste Zeit über prüft es wie erwartet, aber von Zeit zu Zeit liest es out.json in seinem Anfangszustand (""). Ich verlasse mich auf die async-blocker Funktion, um auf process-async vor der Überprüfung zu warten.Inkonsistente Ergebnisse beim Testen einer Chan-Verarbeitungsfunktion

Was ist los mit meinem Ansatz?

(require '[[test-with-files.core :refer [with-files public-dir]]) 

(defn async-blocker [fun & args] 
    (let [chan-test (chan)] 
    (go (>! chan-test (apply fun args))) 
    (<!! chan-test))) 

(defn process-async 
    [channel func] 
    (go-loop [] 
    (when-let [response (<! channel)] 
     (func response) 
     (recur)))) 

(with-files [["/out.json" ""]] 
    (facts "About `process-async" 
      (let [channel (chan) 
       file (io/resource (str public-dir "/out.json")) 
       write #(spit file (str % "\n") :append true)] 
      (doseq [m ["m1" "m2"]] (>!! channel m)) 
      (async-blocker process-async channel write) 
      (clojure.string/split-lines (slurp file)) => (just ["m1" "m2"] :in-any-order) 
      ) 
      ) 
    ) 

Antwort

1

Das Problem ist, dass process-async kehrt sofort mit „[...] ein Kanal, der das Ergebnis des Körpers empfangen wird, wenn abgeschlossen“ (da go-loop nur syntaktischer Zucker für (go (loop ...))go und kehrt sofort).

Dies bedeutet, dass die Blockierung <!! in async-blocker fast sofort einen Wert haben wird und die Reihenfolge, in der die go Blöcke von process-async und async-blocker ausgeführt werden sollen unbestimmt ist. Es kann sein, dass die meiste Zeit der Block von process-async zuerst ausgeführt wird, weil es zuerst erstellt wird, aber das ist nicht viel von einer Garantie in einem gleichzeitigen Kontext.

Gemäß der Dokumentation für <!! es "Wird Null zurückgeben, wenn geschlossen. Wird blockiert, wenn nichts verfügbar ist." Das bedeutet, dass, wenn Sie, dass der Rückgabewert von (apply fun args) annehmen kann ein Kanal durch go zurückgegeben wird, sollten Sie in der Lage sein, durch zu blockieren <!! auf folgende Weise mit:

(defn async-blocker [fun & args] 
    (<!! (apply fun args))) 

Dies wird einmal entsperren gibt es einen Wert im Kanal (dh der Rückgabewert vom go Block).

Es gibt andere Optionen, auf das Ergebnis eines anderen go Blocks zu warten. Sie könnten beispielsweise das Original chan-test als Argument für fun und dann put einen Wert in chan-test bereitstellen, wenn der go-Block, der in fun erstellt wurde, endet. Aber ich denke, angesichts des von Ihnen gezeigten Codes könnten andere Ansätze unnötig komplizierter sein.

Verwandte Themen