Clojure „wielokrotnie” sprawia, że ​​„przyszłość” działa sekwencyjnie

12

Chociaż ten fragment kodu

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

drukuje 10 zmieszanych linii pokazujących różne wątki:

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

zgodnie z oczekiwaniami następujący fragment:

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

produkuje 10 starannie wyrównanych ciągów z tym samym gwintem:

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

co wyraźnie wskazuje, że kontrakty futures nie są prowadzone równolegle, ale w tym samym wątku.

Dzieje się tak tylko repeatedlywtedy, gdy doallnajpierw zdaję sobie sprawę z sekwencji , ale wszystkie wektory, ranges lub inne sekwencje powodują równoległe wykonywanie.

Dlaczego przyszłe wysyłanie do tego samego wątku, gdy repeatedlyzostanie użyte?

Dzięki!

Rick77
źródło

Odpowiedzi:

13

To działa:

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

Problem polega na tym, że rangewytwarza pakietowego sekwencję natomiast repeatedlyprodukuje unchunked sekwencję. Mapa jest leniwa, więc repeatedlyjeśli tworzysz przyszłość, to ją odsuwasz, a następnie tworzysz kolejną przyszłość, a następnie ją odsuwasz. W rangeprzypadku, gdy sekwencja jest podzielona na części, więc tworzysz wszystkie przyszłości, a następnie derefje wszystkie.

Oto kolejny fajny sposób na zaobserwowanie różnicy między zachowaniem sekwencji fragmentu i fragmentu.

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

Rozmiar fragmentów wynosi zwykle 32 (ale myślę, że nigdzie nie jest to gwarantowane), co można zobaczyć, jeśli biegniesz (first (map prn (range 1000))).

Chunking to jedna z tych ukrytych cech Clojure, których zwykle uczysz się, gdy cię gryzie :)

opqdonut
źródło
1
zaraz! [wstaw Conspiracy Keanu Reaves memehere]: Nie widziałem, żeby to nadchodziło! Dziękuję za świetną odpowiedź!
Rick77
1
Nie ma problemu! Widziałem to pytanie tylko dlatego, że opublikowałeś je na #clojure na freenode.
opqdonut