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?

Дякую!

Відповіді:


13

Це працює:

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

Проблема полягає в тому, що rangeвиробляє блочну послідовність , а repeatedlyвиробляє unchunked послідовності. Карта лінива, тож у 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, про які ви зазвичай дізнаєтесь, коли вона вперше кусає вас :)


1
о! [Вставити змову Кіану Рейвз меми]: Я не бачив, щоб це настало! Дякую за чудову відповідь!
Rick77

1
Нема проблем! Я бачив це питання лише тому, що ви розмістили його на #clojure на freenode.
opqdonut
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.