У меня есть 100 сотрудников (агентов), которые делят один ref
, содержащий коллекцию заданий. Хотя эта коллекции есть задача, каждый работник получить одну задачу из этой коллекции (в dosync
блоке), распечатать его, а иногда и положить его обратно в сборе (в dosync
блоке):Странное поведение clojure ref
(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
что я ожидаю. И каждый раз, когда коллекция действительно пуста в конце. Что я делаю неправильно?
Edit:
Я переписал уборщица петлю как можно более простым:
(defn worker-loop
[{:keys [tasks] :as state}]
(loop []
(when-let [task (get-task tasks)]
(println "Task: " task)
(recur)))
state)
Но проблема все еще существует. Этот код ведет себя так, как ожидалось, при создании одного и только одного рабочего.
Является ли 'println' нить безопасной? –
@ShannonSeverance No. Необходимо использовать _e.g._ like '(locking: out (println" ... "))', чтобы дать разборчивый вывод. –