2016-03-17 24 views
1

我有以下代碼:這個參考設置有什麼問題?

(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

問題出在您用(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」...所以現在有一個ID爲2的「a」。現在我們添加「b」,在所有用戶中有1個用戶,所以它將得到id 2 ...與「一個」!!!

當您使用(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作爲一個鍵是一個提示有什麼錯誤。

+0

我也懷疑這一點,並感謝您的確認和解釋。如圖所示,當用戶發生衝突時,用戶總數實際上會減少,這應該是我的線索。 – ekinmur

+0

沒問題,很高興幫助。 –

0

據我所知,無法保證@all-users中的值與(alter all-users ...)調用中的值all-users一致,即使在dosync事務中也是如此。您應該在alter語句中移動整個count-and-create算法。

此外,由於您只處理單個可變實體,因此您可能應該使用​​和swap!