Clojure «неоднократно» заставляет «будущее» работать последовательно

12

Пока этот фрагмент

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

печатает 10 смешанных строк, показывающих разные темы:

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

как и следовало ожидать, следующий фрагмент:

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

производит 10 аккуратно выровненных строк с одинаковым потоком:

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

что ясно указывает на то, что фьючерсы не работают параллельно, а каждый в одном потоке.

Это происходит только с repeatedly, даже если я понимаю последовательность с doallпервым, но векторы, ranges или другие последовательности приводят к параллельному выполнению.

Почему будущая отправка в тот же поток, когда repeatedlyиспользуется?

Спасибо!

Rick77
источник

Ответы:

13

Это работает:

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

Проблема заключается в том, что rangeвыдает чанкованную последовательность, а repeatedlyне чанкованную . Карта ленива, поэтому в repeatedlyслучае, если вы создаете будущее, затем разыменовываете его, затем создаете следующее будущее, затем разыменовываете его. В rangeслучае, если последовательность разделена на части, вы создаете все фьючерсы, а затем создаете их все deref.

Вот еще один интересный способ наблюдать разницу между поведением фрагментированных и непоследовательных последовательностей.

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

Размер кусков обычно составляет 32 (но я думаю, что это нигде не гарантировано), что можно увидеть, если вы запустите (first (map prn (range 1000))).

Чанкинг - это одна из тех скрытых особенностей Clojure, которую вы обычно изучаете, когда она впервые кусает вас :)

opqdonut
источник
1
Вау! [вставить Заговор Киану Ривз (meanhere)): Я не ожидал этого! Спасибо за отличный ответ!
Rick77
1
Нет проблем! Я видел этот вопрос только потому, что вы разместили его на #clojure на freenode.
opqdonut