2015-04-26 3 views
7

Я смотрю на Clojure core.async впервые, и проходил через эту прекрасную презентацию Рича Хики: http://www.infoq.com/presentations/clojure-core-asyncКак очистить каналы core.async от clojure?

У меня был вопрос о примере он показывает в конце своего выступления:

core.async Web Example

Согласно Rich, этот пример в основном пытается получить результат в Интернете, видео и изображении для конкретного запроса. Он пробует два разных источника параллельно для каждого из этих результатов и просто вытаскивает самый быстрый результат для каждого. И вся операция может занимать не более 80 мс, поэтому, если мы не можем получить, например, результат изображения в 80 мс, мы просто сдадимся. «Самая быстрая» функция создает и возвращает новый канал и запускает два режима гонок, чтобы получить результат и поместить его на канал. Затем мы просто берем первый результат из «самого быстрого» канала и шлепаем его по каналу c.

Мой вопрос: что происходит с этими тремя временными неназванными «быстрыми» каналами после того, как мы берем их первый результат? Предположительно, все еще есть процесс go, который припаркован, пытаясь поместить второй результат на канал, но никто не слушает, так что он никогда не завершается. И поскольку канал никогда не связан ни с чем, не похоже, что у нас есть какой-либо способ сделать что-нибудь с ним снова. Будет ли процесс «&» «осознать», что никто больше не заботится о своих результатах и ​​не очистится? Или мы по сути просто «пропустили» три канала/go процессы в этом коде?

ответ

3

Нет утечки.

Припаркованные go s прикреплены к каналам, на которых они пытались выполнить операцию и не имеют независимого существования за пределами этого. Если другой код теряет интерес к каналам, то на него припаркован некий go (NB. A go может одновременно стать клюшкой/принимающим на многих каналах, если он парковки на alt!/alts!), то в конце концов он будет GC'd вместе с теми каналы.

Единственное предостережение заключается в том, что для того, чтобы быть GC'd, go s фактически приходится парковать в первую очередь.Так что любой go, который продолжает делать вещи в петле без парковки (<!/>!/alt!/alts!), на самом деле будет жить вечно. Трудно написать такой код случайно.

+0

Хм, хорошо. Теперь у меня есть два противоречивых ответа от вас и Леона. Вы могли бы предоставить ссылку на свое требование? – Ord

+0

Да, пожалуйста, обратитесь к деталям реализации. Также объясните, как это будет работать в приведенном выше примере кода. –

+0

E. g. возьмите блок go в L4: предположим, что 'c' блокирует put. 'fastest' делает второй набор, который не потребляется. Когда именно, в приведенном выше примере кода есть 'c', а канал, возвращаемый' самым быстрым' мусором, собран? –

1

Предполагается, что канал, созданный fastest, возвращает результат самого быстрого метода запроса и затем закрывается.

Если был произведен второй результат, ваше предположение может привести к утечке процессов fastest. Их результаты никогда не потребляются. Если бы они полагались на все свои результаты, которые должны были быть уничтожены, они не прекращались.

Обратите внимание, что это также может произойти, если в поле alt! выбран канал t.

Обычным способом исправить это было бы закрыть канал c в последнем go блоке с close!. Затем откладываются на закрытый канал, и производители могут прекратить работу.

Проблема также может быть решена при реализации fastest. Процесс, созданный в fastest, сам может сделать ставку через alts! и timeout и прекратить, если произведенные значения не будут потреблены в течение определенного периода времени.

Я думаю, что Rich не рассматривал проблему на слайде в пользу менее продолжительного примера.

+0

Хм, ладно. Теперь у меня есть два противоречивых ответа от вас и Михала. Вы могли бы предоставить ссылку на свое требование? – Ord

+0

Возможно, что заявление Михалса относительно блоков go правильное. Увы, мы не знаем, использует ли реализация «быстрый» использование блоков. Если он порождает потоки, что, вероятно, в случае параллельных поисковых запросов, и блокирует puts через '> !!', эти заброшенные потоки будут оставаться в пуле потоков навсегда, пока JVM не умрет после достаточного количества запросов. –

+0

Это примерная реализация в примере кода для этой презентации, которая просто вызывает (go ... (sleep ...)), поэтому в ответе на этот вопрос использования это бесполезно. –

3

Предостережения и исключения в стороне, вы можете проверить сборку мусора на JVM на REPL.

например:

(require '[clojure.core.async :as async]) 
=> nil 

(def c (async/chan)) 
=> #'user/c 
(def d (async/go-loop [] 
     (when-let [v (async/<! c)] 
      (println v) 
      (recur)))) 
=> #'user/d 

(async/>!! c :hi) 
=> true 
:hi  ; core.async go block is working 

(import java.lang.ref.WeakReference) 
=> java.lang.ref.WeakReference ; hold a reference without preventing garbage collection 
(def e (WeakReference. c)) 
=> #'user/e 
(def f (WeakReference. d)) 
=> #'user/f 

(.get e) 
=> #object[...] 
(.get f) 
=> #object[...] 

(def c nil) 
=> #'user/c 
(def d nil) 
=> #'user/d 
(println "We need to clear *1, *2 and *3 in the REPL.") 
We need to clear *1, *2 and *3 in the REPL. 
=> nil 
(println *1 *2 *3) 
nil #'user/d #'user/c 
=> nil 
(System/gc) 
=> nil 
(.get e) 
=> nil 
(.get f) 
=> nil 

Что произошло? Я настраиваю блок go и проверяю, что он работает. Затем использовали WeakReference для наблюдения за каналом связи (c) и обратным каналом возврата (d). Затем я удалил все ссылки на c и d (включая *1, *2 и *3, созданные моим REPL), запросил сбор мусора (и повезло, System.gc Javadoc не дает сильных гарантий), а затем заметил, что мои слабые ссылки были очищены.

В этом случае, по крайней мере, один раз ссылки на каналы, участвующих было удалено, каналы были мусор (независимо от моего отказа, чтобы закрыть их!)

Смежные вопросы