2015-04-26 5 views
7

ich bei Clojure core.async zum ersten Mal suchen, und wurde durch diese hervorragende Präsentation gehe von Rich Hickey: http://www.infoq.com/presentations/clojure-core-asyncWie werden die core.async-Kanäle von clojure aufgeräumt?

Ich hatte eine Frage über das Beispiel, das er am Ende seiner Präsentation zeigt:

core.async Web Example

Laut Reich, in diesem Beispiel versucht im Grunde ein Web, Video zu bekommen, und Bildergebnis für eine bestimmte Abfrage. Er probiert zwei verschiedene Quellen parallel für jedes dieser Ergebnisse aus und zieht für jeden das schnellste Ergebnis. Und die gesamte Operation kann nicht mehr als 80 ms dauern, wenn wir also z.B. Ein Image-Ergebnis in 80ms, wir geben einfach auf. Die "schnellste" Funktion erstellt und gibt einen neuen Kanal zurück und startet zwei Go-Prozesse, um ein Ergebnis zu erhalten und es auf den Kanal zu setzen. Dann nehmen wir einfach das erste Ergebnis vom "schnellsten" Kanal und schlagen es auf den c-Kanal.

Meine Frage: Was passiert mit diesen drei temporären, unbenannten "schnellsten" Kanälen, nachdem wir ihr erstes Ergebnis gemacht haben? Vermutlich gibt es noch einen Go-Prozess, der geparkt ist, um das zweite Ergebnis auf den Kanal zu legen, aber niemand hört zu, so dass es nie wirklich abgeschlossen wird. Und da der Kanal nie an irgendetwas gebunden ist, scheint es nicht so, als hätten wir jemals wieder etwas damit zu tun. Wird der Prozess & Kanal "realisieren", dass niemand sich mehr um ihre Ergebnisse kümmert und sich aufräumt? Oder haben wir im Wesentlichen nur drei Kanäle/Prozesse in diesem Code "durchlecken"?

Antwort

3

Es gibt kein Leck.

Geparkt go s sind an Kanäle angeschlossen, auf denen sie versucht haben, eine Operation auszuführen, und haben darüber hinaus keine unabhängige Existenz. Wenn ein anderer Code das Interesse an den Kanälen verliert, wird ein bestimmter go geparkt (NB. Ein go kann gleichzeitig ein Putter/Nehmer auf vielen Kanälen werden, wenn er auf /alts! parkt), dann wird es schließlich zusammen mit diesen Kanäle.

Der einzige Nachteil besteht darin, dass go s tatsächlich zuerst parken müssen, um GC'd zu sein.So dass jeder go, die ohne jemals Park Dinge tun, in einer Schleife hält (<!/>!/alt!/alts!) wird in der Tat für immer leben. Es ist jedoch schwer, diese Art von Code aus Versehen zu schreiben.

+0

Hmm, okay. Ich habe jetzt zwei widersprüchliche Antworten von dir und Leon. Wären Sie in der Lage gewesen, eine Referenz für Ihren Anspruch bereitzustellen? – Ord

+0

Ja, bitte Link zu den Implementierungsdetails. Bitte erläutern Sie auch, wie dies im obigen Codebeispiel funktionieren würde. –

+0

E. g. nimm den Go-Block in L4: Angenommen, 'c' blockiert den Put. "Schnellster" macht einen zweiten Satz, der nicht verbraucht wird. Wann genau, in dem obigen Codebeispiel, sind "c" und der Kanal von "schnellste" Müll gesammelt zurückgegeben? –

1

Vermutlich gibt ein Kanal, der von fastest produziert wird, nur das Ergebnis der schnellsten Abfrage-Methode zurück und schließt dann.

Wenn ein zweites Ergebnis erzeugt wurde, könnte Ihre Annahme gelten, dass die fastest Prozesse durchgesickert sind. Ihre Ergebnisse werden niemals verbraucht. Wenn sie sich darauf verlassen würden, dass all ihre Ergebnisse konsumiert werden, um zu enden, würden sie nicht enden.

Beachten Sie, dass dies auch dann, wenn der Kanal t in der alt! Klausel ausgewählt passieren könnte.

usualy Die Möglichkeit, dies zu beheben mit close! den Kanal c im letzten go Block zu schließen wäre. Puts, die zu einem geschlossenen Kanal gemacht wurden, werden dann fallen gelassen und die Produzenten können enden. Das Problem könnte auch in der Implementierung von fastest gelöst werden. Der in fastest erstellte Prozess könnte sich selbst über alts! und timeout setzen und beenden, wenn die produzierten Werte nicht innerhalb einer bestimmten Zeit verbraucht werden.

Ich denke, Rich hat das Problem auf der Folie nicht zugunsten eines weniger langen Beispiels angesprochen.

+0

Hmm, okay. Ich habe jetzt zwei widersprüchliche Antworten von dir und Michal. Wären Sie in der Lage gewesen, eine Referenz für Ihren Anspruch bereitzustellen? – Ord

+0

Es könnte sein, dass Michals Aussage bezüglich der Go-Blöcke richtig ist. Leider wissen wir nicht, ob die Implementierung von "schnellsten" Anwendungen Blöcke macht. Wenn es Threads erzeugt, was im Fall von gleichzeitigen Suchabfragen wahrscheinlich ist und Blockierungen über '> !!' setzt, bleiben diese abgebrochenen Threads für immer im Thread-Pool, bis die JVM nach genügend Anfragen abstirbt. –

+0

Es ist eine Dummy-Implementierung in dem Beispielcode für diese Präsentation, die nur anruft (go ... (sleep ...) erledigt), so dass sie als Referenz bei der Beantwortung dieser Frage keine Verwendung hat. –

3

Caveats und Ausnahmen abgesehen, können Sie Garbage Collection auf der JVM an der REPL testen.

zB:

(require '[clojure.core.async :as async]) 
=> nil 

(def c (async/chan)) 
=> #'user/c 
(def d (async/go-loop [] 
     (when-let [v (async/<! c)] 
      (println v) 
      (recur)))) 
=> #'user/d 

(async/>!! c :hi) 
=> true 
:hi  ; core.async go block is working 

(import java.lang.ref.WeakReference) 
=> java.lang.ref.WeakReference ; hold a reference without preventing garbage collection 
(def e (WeakReference. c)) 
=> #'user/e 
(def f (WeakReference. d)) 
=> #'user/f 

(.get e) 
=> #object[...] 
(.get f) 
=> #object[...] 

(def c nil) 
=> #'user/c 
(def d nil) 
=> #'user/d 
(println "We need to clear *1, *2 and *3 in the REPL.") 
We need to clear *1, *2 and *3 in the REPL. 
=> nil 
(println *1 *2 *3) 
nil #'user/d #'user/c 
=> nil 
(System/gc) 
=> nil 
(.get e) 
=> nil 
(.get f) 
=> nil 

Was gerade passiert ist? Ich richtete einen Go-Block ein und überprüfte, ob es funktionierte. Verwenden Sie dann eine WeakReference, um den Kommunikationskanal (c) und den Go-Block-Rückkanal (d) zu beobachten. Dann entfernte ich alle Verweise auf c und d (einschließlich *1, *2 und *3 erstellt von meinem REPL), angeforderte Müllabfuhr, (und hatte Glück, die System.gc Javadoc macht keine starken Garantien) und dann beobachtet, dass meine schwache Referenzen gelöscht worden war.

In diesem Fall zumindest einmal Verweise auf die Kanäle entfernt worden waren beteiligt waren die Kanäle Müll gesammelt (unabhängig davon, mein Versagen, sie zu schließen!)