2015-04-26 14 views
7

sto guardando Clojure core.async per la prima volta, e stava attraversando questo eccellente presentazione da Rich Hickey: http://www.infoq.com/presentations/clojure-core-asyncCome si eliminano i canali core.async di clojure?

avevo una domanda circa l'esempio che mostra al termine della sua presentazione:

core.async Web Example

Secondo Rich, questo esempio tenta fondamentalmente di ottenere un risultato Web, video e immagine per una query specifica. Prova due fonti diverse in parallelo per ognuno di questi risultati, e tira fuori il risultato più veloce per ciascuno. E l'intera operazione non può richiedere più di 80 ms, quindi se non possiamo ottenere ad es. un risultato di immagine in 80 ms, ci arrenderemo. La funzione "più veloce" crea e restituisce un nuovo canale e avvia due processi di corsa racing per recuperare un risultato e inserirlo nel canale. Quindi prendiamo il primo risultato dal canale 'più veloce' e lo schiaffiamo sul canale c.

La mia domanda: cosa succede a questi tre canali temporali, senza nome, "più veloci", dopo il primo risultato? Presumibilmente c'è ancora un processo di go che è parcheggiato cercando di mettere il secondo risultato sul canale, ma nessuno sta ascoltando, quindi non è mai stato effettivamente completato. E poiché il canale non è mai legato a nulla, non sembra che abbiamo mai avuto modo di fare qualcosa con esso. Il processo di go processing & "realizzerà" che a nessuno importa più dei loro risultati e si pulisce? O abbiamo essenzialmente "perso" tre canali/processi in questo codice?

risposta

3

non ci siano perdite.

parcheggiate go s sono attaccati ai canali sui quali hanno tentato di eseguire un'operazione e non hanno alcuna esistenza indipendente di là di questo. Se un altro codice perde interesse per i canali, viene parcheggiato un certo go (NB: un go può diventare contemporaneamente un putter/taker su molti canali se viene parcheggiato su alt!/alts!), quindi alla fine sarà GC con quelli canali.

L'unica avvertenza è che per essere GC'd, go s in realtà hanno a parcheggiare prima.Quindi qualsiasi go che continua a fare cose in un loop senza mai parcheggiare (<!//alts!), infatti, vivrà per sempre. Però è difficile scrivere questo tipo di codice per caso.

+0

Hmm, ok. Ora ho due risposte contrastanti da te e Leon. Saresti stato in grado di fornire un riferimento per il tuo reclamo? – Ord

+0

Sì, si prega di collegare ai dettagli di implementazione. Inoltre, ti preghiamo di spiegare come funziona nel codice sopra riportato. –

+0

E. g. prendi il blocco go in L4: supponiamo che 'c' stia bloccando il put. 'più veloce' fa un secondo put che non viene consumato. Quando esattamente, nell'esempio di codice sopra, sono 'c' e il canale restituito dalla raccolta dei dati più veloce raccolta? –

1

Assumendo che un canale prodotto da fastest restituisca solo il risultato del metodo di query più veloce e quindi si chiuda.

Se è stato prodotto un secondo risultato, è possibile ipotizzare che i processi fastest siano trapelati. I loro risultati non vengono mai consumati. Se si affidassero a tutti i risultati da consumare per terminare, non si risolverebbero.

Si noti che questo potrebbe verificarsi anche se il canale t è selezionato nella clausola alt!.

Il solito modo di risolvere questo problema è chiudere il canale c nell'ultimo blocco go con close!. I put realizzati su un canale chiuso verranno quindi eliminati e i produttori potranno terminare.

Il problema potrebbe anche essere risolto nell'implementazione di fastest. Il processo creato in fastest stessa potrebbe rendere put via alts! e timeout e terminare se i valori prodotti non vengono consumati entro un certo periodo di tempo.

immagino Rich non ha affrontato il problema nella diapositiva in favore di una meno lungo esempio.

+0

Hmm, ok. Ora ho due risposte contrastanti da te e Michal. Saresti stato in grado di fornire un riferimento per il tuo reclamo? – Ord

+0

Potrebbe essere che la dichiarazione di Michal riguardo ai blocchi di partenza sia corretta. Purtroppo non sappiamo se l'implementazione degli usi 'più veloci' diventino blocchi. Se genera thread, il che è probabile nel caso di query di ricerca simultanee e blocca il caricamento tramite '> !! 'questi thread abbandonati rimarranno nel pool di thread per sempre fino a quando la JVM non muore dopo un numero sufficiente di richieste. –

+0

È un'implementazione fittizia nel codice di esempio per quella presentazione che chiama semplicemente (vai ... (sleep ...) fatto quindi è inutile come riferimento nel rispondere a questa domanda d'uso. –

3

Avvertenze ed eccezioni a parte, è possibile testare la garbage collection su JVM al REPL.

esempio:

(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 

Cosa è appena successo? Ho impostato un blocco go e ho verificato che funzionava. Quindi utilizzare WeakReference per osservare il canale di comunicazione (c) e il canale di ritorno blocco go (d). Quindi ho rimosso tutti i riferimenti a c e d (incluso, *2 e *3 creati dal mio REPL), richiesto garbage collection, (e ho avuto fortuna, il System.gc Javadoc non fornisce forti garanzie) e poi ho osservato che i miei riferimenti deboli erano stati cancellati.

In questo caso, almeno, una volta i riferimenti ai canali coinvolti era stato rimosso, i canali erano garbage collection (a prescindere dalla mia incapacità di chiuderli!)

Problemi correlati