У меня есть 100 рабочих (агентов), которые совместно используют один ref
, содержащий набор задач. Пока в этой коллекции есть задачи, каждый воркер получает одну задачу из этой коллекции (в блоке dosync
), распечатывает ее и иногда кладет обратно в коллекцию (в блоке dosync
):
(defn have-tasks?
[tasks]
(not (empty? @tasks)))
(defn get-task
[tasks]
(dosync
(let [task (first @tasks)]
(alter tasks rest)
task)))
(defn put-task
[tasks task]
(dosync (alter tasks conj task))
nil)
(defn worker
[& {:keys [tasks]}]
(agent {:tasks tasks}))
(defn worker-loop
[{:keys [tasks] :as state}]
(while (have-tasks? tasks)
(let [task (get-task tasks)]
(println "Task: " task)
(when (< (rand) 0.1)
(put-task tasks task))))
state)
(defn create-workers
[count & options]
(->> (range 0 count)
(map (fn [_] (apply worker options)))
(into [])))
(defn start-workers
[workers]
(doseq [worker workers] (send-off worker worker-loop)))
(def tasks (ref (range 1 10000000)))
(def workers (create-workers 100 :tasks tasks))
(start-workers workers)
(apply await workers)
Когда я запускаю этот код, последнее значение, напечатанное агентами (после нескольких попыток): 435445
, 4556294
, 1322061
, 3950017
. Но никогда не 9999999
то, что я ожидаю. И каждый раз коллекция действительно пуста в конце. Что я делаю неправильно?
Изменить:
Я переписал рабочий цикл как можно проще:
(defn worker-loop
[{:keys [tasks] :as state}]
(loop []
(when-let [task (get-task tasks)]
(println "Task: " task)
(recur)))
state)
Но проблема все еще существует. Этот код ведет себя так, как ожидалось, при создании одного и только одного рабочего.
println
потокобезопасным? - person Shannon Severance   schedule 23.08.2016(locking :out (println "..."))
, чтобы получить разборчивый вывод. - person Terje D.   schedule 23.08.2016