Clojure "repetidamente" hace que el "futuro" se ejecute secuencialmente

12

Mientras este fragmento

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

Imprime 10 líneas entremezcladas que muestran diferentes hilos:

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]]]

como era de esperar, el siguiente fragmento:

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

produce 10 cadenas perfectamente alineadas con el mismo hilo:

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]]                          

lo que indica claramente que los futuros no se ejecutan en paralelo, sino cada uno en el mismo hilo.

Esto sucede solo con repeatedly, incluso si me doy cuenta de la secuencia con doallprimero, pero los vectores, rangeso otras secuencias dan como resultado una ejecución paralela.

¿Por qué se envía en el futuro al mismo hilo cuando repeatedlyse usa?

¡Gracias!

Rick77
fuente

Respuestas:

13

Esto funciona:

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

El problema es que rangeproduce una secuencia fragmentada mientras que repeatedlyproduce una secuencia no fragmentada . El mapa es perezoso, por lo que en el repeatedlycaso de que estés creando un futuro, luego lo desredes, luego crees el próximo futuro y luego lo desrefines. En el rangecaso de que la secuencia esté fragmentada, está creando todos los futuros y luego dereftodos ellos.

Aquí hay otra forma divertida de observar la diferencia entre el comportamiento de secuencias fragmentadas y no fragmentadas.

=> (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

El tamaño de los trozos suele ser 32 (pero creo que eso no está garantizado en ningún lado), como se puede ver si corres (first (map prn (range 1000))).

Chunking es una de esas características ocultas de Clojure que generalmente aprendes cuando te muerde por primera vez :)

opqdonut
fuente
1
whoa! [insertar Conspiracy Keanu Reaves memehere]: ¡No lo vi venir! Gracias por la gran respuesta!
Rick77
1
¡No hay problema! Solo vi esta pregunta porque la publicaste en #clojure en freenode.
opqdonut