Clojure "repetidamente" faz com que o "futuro" seja executado sequencialmente

12

Enquanto esse trecho

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

imprime 10 linhas misturadas mostrando diferentes segmentos:

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 eu esperava, o seguinte snippet:

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

produz 10 cordas ordenadamente alinhadas com o mesmo encadeamento:

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

o que indica claramente que os futuros não são executados em paralelo, mas cada um no mesmo segmento.

Isso acontece apenas com repeatedly, mesmo que eu realize a sequência com a doallprimeira, mas vetores, ranges ou outras seqüências resultam em execução paralela.

Por que o envio futuro para o mesmo encadeamento quando repeatedlyé usado?

Obrigado!

Rick77
fonte

Respostas:

13

Isso funciona:

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

O problema é que rangeproduz uma sequência fragmentada enquanto repeatedlyproduz uma sequência não fragmentada . O mapa é preguiçoso; portanto, no repeatedlycaso de você criar um futuro, desrefeitá-lo, criar o próximo futuro e desrefeitá-lo. No rangecaso de a sequência ser dividida, você cria todos os futuros e, em seguida, dereftodos eles.

Aqui está outra maneira divertida de observar a diferença entre o comportamento de seqüências em pedaços e em pedaços.

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

O tamanho dos pedaços geralmente é 32 (mas acho que isso não é garantido em nenhum lugar), como pode ser visto se você executar (first (map prn (range 1000))).

Chunking é um daqueles recursos ocultos do Clojure que você geralmente aprende quando o morde pela primeira vez :)

opqdonut
fonte
11
Uau! [inserir Conspiração Keanu Reaves memehere]: Eu não vi isso chegando! Obrigado pela ótima resposta!
Rick77 6/03
11
Sem problemas! Eu só vi essa pergunta porque você a postou em #clojure on freenode.
opqdonut 6/03