Reputation: 79
I'm trying to update a nested counter in an atom (a map) from multiple threads, but getting unpredictable results.
(def a (atom {:id {:counter 0}}))
(defn upvote [id]
(swap! a assoc-in [(keyword id) :counter] (inc (get-in @a [(keyword id) :counter])))
)
(dotimes [i 10] (.start (Thread. (fn [] (upvote "id")))))
(Thread/sleep 12000)
(prn @a)
I'm new to Clojure so very possible I'm doing something wrong, but can't figure out what. It's printing a counter value with results varying from 4-10, different each time.
I want to atomically update the counter value and hoped that this approach would always give me a counter value of 10. That it would just retry upon failure and eventually get to 10.
It's for an up-vote function that can get triggered concurrently.
Can you see what I'm doing wrong here?
Upvotes: 0
Views: 815
Reputation: 5395
You are updating the atom non-atomically in your code. You first get its value by @a
, and then apply it using the swap
function. The value may change in between.
The atomic way to update the value is to use a pure function within swap, without referring to the previous atom value via @
:
(defn upvote [id]
(swap! a update-in [(keyword id) :counter] inc))
Upvotes: 6