在Clojure中同步读写的方法?

Phi*_*sky 6 synchronization state transactions clojure stm

在Web应用程序中,我正在尝试从有限的ID池中生成唯一的线程安全ID.我面临的问题是,在读写之间,另一个线程可能已经改变了数据结构; 这就是我不得不诉诸的原因compare-and-set!.

(def sid-batch 10)
(def sid-pool (atom {:cnt 0
                     :sids '()}))

(defn get-sid []
  (let [{:keys [cnt sids] :as old} @sid-pool]

    ; use compare-and-set! here for atomic read & write
    (if (empty? sids)

      ; generate more sids
      (if (compare-and-set!
            sid-pool
            old
            (-> old
              (assoc :sids (range (inc cnt) (+ sid-batch cnt)))
              (assoc :cnt (+ cnt sid-batch))))

        ; return newest sid or recur till "transaction" succeeds
        cnt
        (recur))

      ; get first sid
      (if (compare-and-set! sid-pool old (update-in old [:sids] next))

        ; return first free sid or recur till "transaction" succeeds
        (first sids)
        (recur)))))
Run Code Online (Sandbox Code Playgroud)

是否有一种更简单的方法来同步读取和写入,而无需"手动"执行STM并且不会滥用字段sid-pool作为返回值swap!

ama*_*loy 5

你可以用一个原子来做,通过sid-pool你想象的方式添加一个字段.我同意这有点粗略,但是使用compare-and-swap!这么简单的东西就是神圣的.相反,使用原子; 或者一个参考,它允许你从一个dosync区块返回你想要的任何东西,同时仍然是交易安全的:

(defn get-sid []
  (dosync
   (let [{:keys [cnt sids]} @sid-pool]
     (if (empty? sids)
       (do 
         (alter sid-pool
                (fn [old]
                  (-> pool
                      (assoc :sids (range (inc cnt) (+ sid-batch cnt)))
                      (update-in [:cnt] + sid-batch))))
         cnt)
       (do
         (alter sid-pool update-in [:sids] next)
         (first sids))))))
Run Code Online (Sandbox Code Playgroud)