我有以下代码:
(ns fwpd.core
(:import java.util.concurrent.Executors))
(def thread-pool
(Executors/newFixedThreadPool
(+ 2 (.availableProcessors (Runtime/getRuntime)))))
(defn dothreads!
[f & {thread-count :threads
exec-count :times
:or {thread-count 1 exec-count 1}}]
(dotimes [t thread-count]
(.submit thread-pool
#(dotimes [_ exec-count] (f)))))
(def all-users (ref {}))
(defn new-user [id login monthly-budget]
{:id id
:login login
:monthly-budget monthly-budget
:total-expense 0})
(defn add-new-user [login monthly-budget]
(dosync
(let [current-user (count @all-users)
user (new-user (inc current-user) login monthly-budget)]
(alter all-users assoc login user))))
当我在REPL中加载它并使用以下命令运行时:
(dothreads! #(add-new-user (str (rand-int 500) "name") 5000) :threads 4 :times 4)
我看到我有时会获得具有相同ID的用户,尽管这些名称是随机生成的,并且不会像我预期的那样发生冲突。
我到底错过了什么?
答案 0 :(得分:0)
据我所知,无法保证@all-users
中的值与all-users
调用中(alter all-users ...)
的值一致,即使在dosync事务中也是如此。您应该在alter语句中移动整个count-and-create算法。
此外,由于您只处理单个可变实体,因此您可能应该atom
使用swap!
。
答案 1 :(得分:0)
问题在于您使用(rand-int 500)
生成的名称。
您看到的行为没有任何并发性,这是将ID分配给现有用户的方式中的缺陷。
考虑一下:
(add-new-user "a" 100)
(add-new-user "a" 100)
(add-new-user "b" 100)
(prn @all-users)
=> {"a" {:id 2, :login "a", :monthly-budget 100, :total-expense 0},
"b" {:id 2, :login "b", :monthly-budget 100, :total-expense 0}}
这里发生了什么?我们创建了用户" a"当所有用户都没有,所以" a"获得id 1.然后我们创建了另一个用户" a"它将获得id 2,但是当你添加新的" a"在地图上,它取代了旧的" a" ...所以现在有一个" a" ID为2.现在我们添加" b",在所有用户中有1个用户,因此它将获得id 2 ...与" a" !!!
当您使用(rand-int 500)
时,您可能会收到相同的号码。您可以使用(rand-int 5)
或50或500000查看效果。为什么不使用ID作为密钥呢?
(defn add-new-user [login monthly-budget]
(dosync
(let [current-user (count @all-users)
id (inc current-user)
user (new-user id login monthly-budget)]
(alter all-users assoc id user))))
(dotimes [i 10]
(prn "START")
(dothreads! #(add-new-user (str (rand-int 10) "name") 5000) :threads 4 :times 4)
(Thread/sleep 1000)
(prn "COUNT" (count @all-users))
(if (apply distinct? (map :id (vals @all-users)))
(prn "Distinct ids")
(prn "*** NOT distinct ***" (sort (map :id (vals @all-users))))))
=>
"START"
"COUNT" 16
"Distinct ids"
"START"
"COUNT" 32
"Distinct ids"
...
另请注意,通常名称不一定是唯一的,但ID确实如此。因此,当你有一个id时,使用name作为键是一个提示是错误的。