Clojure «à plusieurs reprises» fait fonctionner le «futur» de manière séquentielle

12

Bien que cet extrait

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

imprime 10 lignes mélangées montrant différents fils:

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

comme je m'y attendais, l'extrait suivant:

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

produit 10 chaînes soigneusement alignées avec le même fil:

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

ce qui indique clairement que les futures ne se déroulent pas en parallèle, mais chacun dans le même fil.

Cela ne se produit qu'avec repeatedly, même si je réalise la séquence avec d' doallabord, mais les vecteurs, ranges ou autres séquences entraînent tous une exécution parallèle.

Pourquoi le futur envoi vers le même thread repeatedlyest-il utilisé?

Merci!

Rick77
la source

Réponses:

13

Cela marche:

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

Le problème est que rangeproduit une séquence fragmentée tout en repeatedlyproduisant une séquence non fragmentée . La carte est paresseuse, donc dans le repeatedlycas où vous créez un futur, puis le déréférez, puis créez le futur suivant, puis le dereffez. Dans le rangecas où la séquence est fragmentée, vous créez tous les futurs, puis dereftous.

Voici une autre façon amusante d'observer la différence entre le comportement des séquences fragmentées et non bloquées.

=> (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 taille des morceaux est généralement de 32 (mais je pense que ce n'est garanti nulle part), comme vous pouvez le voir si vous exécutez (first (map prn (range 1000))).

Le chunking est l'une de ces fonctionnalités cachées de Clojure que vous apprenez généralement quand il vous mord pour la première fois :)

opqdonut
la source
1
whoa! [insérer le complot memehere de Keanu Reaves]: Je ne l'ai pas vu venir! Merci pour la bonne réponse!
Rick77
1
Aucun problème! Je n'ai vu cette question que parce que vous l'avez publiée sur #clojure sur freenode.
opqdonut