Je suis en train de regarder Clojure Core.Async pour la première fois et suivait cette excellente présentation de Rich Hickey: http://www.infoq.com/presentions/clojure-core-async p>
J'ai une question sur l'exemple qu'il affiche à la fin de sa présentation: P>
P>
Selon riche, cet exemple essaie essentiellement d'obtenir un résultat Web, vidéo et d'image pour une requête spécifique. Il essaie deux sources différentes en parallèle pour chacun de ces résultats et retire simplement le résultat le plus rapide pour chacun. Et toute l'opération ne peut prendre plus de 80 ms, donc si nous ne pouvons pas obtenir par exemple. Une image aboutit à 80MS, nous allons abandonner. La fonction "la plus rapide" crée et renvoie une nouvelle chaîne et commence deux processus Go Course pour récupérer un résultat et le mettre sur le canal. Ensuite, nous venons de prendre le premier résultat du canal "le plus rapide" et de la gifler sur le canal C. P>
Ma question: Qu'advient-il de ces trois canaux temporaires «les plus rapides» non nominés après avoir pris leur premier résultat? Vraisemblablement, il y a toujours un processus garé qui est garé en essayant de mettre le deuxième résultat sur le canal, mais personne n'écoute donc que cela ne finit jamais. Et puisque la chaîne n'est jamais liée à rien, il ne semble pas que nous ayons un moyen de faire quoi que ce soit avec elle de nouveau. Est-ce que le processus Go Process & Channel "se rendra compte" que personne ne se soucie de leurs résultats et de se nettoyer? Ou avons-nous essentiellement simplement "fuite" trois processus de canaux / go dans ce code? P>
3 Réponses :
Un canal produit avec justement produit par Si un deuxième résultat a été produit, votre hypothèse pourrait contenir que les processus Notez que cela pourrait également se produire si le canal Le moyen habituel de résoudre ce problème serait de fermer le canal Le problème pourrait également être résolu dans la mise en œuvre de Je suppose que Rich n'a pas résolu le problème dans la glissière en faveur d'un exemple moins long. P> le plus rapide code> ne renvoie que le résultat de la méthode de la requête la plus rapide puis ferme. P>
les plus rapides code> sont divulgués. Leurs résultats ne sont jamais consommés. S'ils s'appuient sur tous leurs résultats à consommer pour terminer, ils ne se termineraient pas. P>
t code> est sélectionné dans la clause alt! code>. p>
C code> dans le dernier bloc code> go code> avec Fermer! code>. Les mises à jour à un canal fermé seront ensuite abandonnées et les producteurs peuvent se terminer. p>
le plus rapide code>. Le processus créé dans le plus rapide code> pourrait lui rendre le put via alts! Code> et délai d'attente code> et terminer si les valeurs produites ne sont pas consommées dans un certain montant de temps. p>
Hmm d'accord. J'ai maintenant deux réponses conflictuelles de votre part et de Michal. Seriez-vous pu fournir une référence pour votre demande?
Ce serait peut-être que la déclaration de Michals concernant Go Blocks a raison. Hélas Nous ne savons pas si la mise en œuvre de le plus rapide code> utilise des blocs Go. Si elle abonnaît des threads, ce qui est probable dans le cas de requêtes de recherche simultanées, et bloque les mises via > !! code> ces threads abandonnés vont rester dans la piscine de fil pour toujours jusqu'à ce que la JVM meurt après suffisamment de demandes .
C'est une implémentation factice dans l'exemple de code de cette présentation qui appelle seulement (aller ... (Dormir ...) Terminé) Il n'est donc pas utile de faire référence à la réponse à cette question d'utilisation.
Strictement parlant alors, la réponse de Michals est correcte cependant. Je ne savais pas qu'une mise en œuvre factice a été fournie.
Il n'y a pas de fuite. P>
gaked La seule mise en garde est que pour être GC'D, go code> s sont attachés aux canaux sur lesquels ils ont tenté d'effectuer une opération et n'ont aucune existence indépendante au-delà de cela. Si autre code perd l'intérêt dans les canaux, un certain go code> est garé sur (NB alt! code> / alts! code>), puis éventuellement ce sera gc'd avec ces canaux. P>
GO CODE> S doit réellement se garer en premier. Donc, tout aller code> qui continue de faire des choses dans une boucle sans jamais stationnement ( / >! Code> / alt! Code> / alts! code>) va en fait vivre pour toujours. Il est difficile d'écrire ce type de code par accident, cependant. P>
Hmm d'accord. J'ai maintenant deux réponses conflictuelles de votre part et de Leon. Seriez-vous pu fournir une référence pour votre demande?
Oui, veuillez créer un lien vers les détails de la mise en œuvre. En outre, veuillez expliquer comment cela fonctionnerait dans l'échantillon de code ci-dessus.
Par exemple. Prenez le bloc Go en L4: supposez C code> bloque la mise en place. Le plus rapide code> fait une deuxième mise en place qui n'est pas consommée. Quand exactement, dans l'échantillon de code ci-dessus, sont C code> et le canal renvoyé par la plus rapide code> déchets collectés?
Mise en garde et exceptions de côté, vous pouvez tester la collection de déchets sur la JVM sur la RÉPL.
EG: P>
(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