Clojure "ripetutamente" fa funzionare il "futuro" in sequenza


12

Mentre questo frammento

(dorun 
  (map deref 
    (map #(future 
            (println % (Thread/currentThread))) 
         (range 10))))

stampa 10 linee mescolate che mostrano diversi thread:

0 #object[java.lang.Thread 0x5f1b4a83 Thread[clojure-agent-send-off-pool-26,5,main]]                                                                                                                           
2 #object[java.lang.Thread 1 0x79dfba1f #object[Thread[clojure-agent-send-off-pool-28,5,main]java.lang.Thread]                                                                                                 
3 4 #object[java.lang.Thread #object[java.lang.Thread 0x7ef7224f Thread[clojure-agent-send-off-pool-27,5,main]0x5f1b4a83 ]Thread[clojure-agent-send-off-pool-26,5,main]]                                       
5                                                                                                                                                                                                              
67  #object[java.lang.Thread #object[0x79dfba1f java.lang.Thread Thread[clojure-agent-send-off-pool-28,5,main]]0x77526645                                                                                      
 8 #object[java.lang.Thread #object[java.lang.ThreadThread[clojure-agent-send-off-pool-29,5,main] ]9 #object[java.lang.Thread 0xc143aa5 0x7ef7224f                                                             Thread[clojure-agent-send-off-pool-31,5,main]]Thread[clojure-agent-send-off-pool-27,5,main]]                                                                                                                       

0x1ce8675f 0x379ae862 Thread[clojure-agent-send-off-pool-30,5,main]Thread[clojure-agent-send-off-pool-32,5,main]]]

come mi aspetterei, il seguente frammento:

(dorun
  (map deref 
    (map #(future 
            (println % (Thread/currentThread))) 
         (repeatedly 10 #(identity 42)))))

produce 10 stringhe ben allineate con lo stesso thread:

42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                                                                                                                              
42 #object[java.lang.Thread 0x1e1b7ffb Thread[clojure-agent-send-off-pool-39,5,main]]                          

che indica chiaramente che i futures non sono gestiti in parallelo, ma ciascuno nella stessa discussione.

Questo accade solo con repeatedly, anche se realizzo la sequenza con la doallprima, ma i vettori, le rangealtre sequenze si traducono in un'esecuzione parallela.

Perché in futuro verrà inviato allo stesso thread quando repeatedlyviene utilizzato?

Grazie!

Risposte:


13

Questo funziona:

(dorun (map deref (doall (map #(future (println % (Thread/currentThread))) (repeatedly 10 #(identity 42))))))

Il problema è che rangeproduce una sequenza chunked mentre repeatedlyproduce una sequenza unchunked . La mappa è pigra, quindi nel repeatedlycaso tu stia creando un futuro, quindi derefandolo, quindi creando il prossimo futuro, quindi dereffandolo. Nel rangecaso in cui la sequenza sia frammentata, quindi stai creando tutti i futures e poi derefli ingeni tutti.

Ecco un altro modo divertente per osservare la differenza tra il comportamento delle sequenze in blocchi e quelle non in blocchi.

=> (first (map prn (range 10)))
0
1
2
3
4
5
6
7
8
9
nil
=> (first (map prn (repeatedly 10 #(identity 13))))
13
nil

La dimensione dei blocchi è di solito 32 (ma penso che non sia garantita da nessuna parte), come si può vedere se si esegue (first (map prn (range 1000))).

Chunking è una di quelle caratteristiche nascoste di Clojure che di solito impari quando ti morde per la prima volta :)


1
Whoa! [inserire la cospirazione Keanu Reaves memehere]: Non l'ho visto arrivare! Grazie per l'ottima risposta!
Rick77,

1
Nessun problema! Ho visto questa domanda solo perché l'hai pubblicata su #clojure su freenode.
opqdonut,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.