在Clojure中同步读取和写入的方式?

在一个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)))))

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


你可以用一个原子来做到这一点,通过你似乎暗示的方式向sid-pool添加一个字段。 我同意这有点粗俗,但使用compare-and-swap! 对于这么简单的事情来说,这是一个苦苦挣扎 相反,使用原子; 或者一个ref,它可以让你从dosync块中返回任何你想要的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))))))

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

(defn get-sid []
  (first (:sids (swap! sid-pool
                  (fn [{:keys [cnt sids]}]
                    (if-let [sids (next sids)]
                      {:cnt cnt :sids sids}
                      {:sids (range cnt (+ sid-batch cnt))
                       :cnt (+ cnt sid-batch)}))))))

就像我在评论中所说的那样,我认为你有正确的想法:“滥用sid-pool中的字段”。 除了你不需要一个字段,只需调用(comp第一个sids)来自swap的返回值!

我将呼叫中的公司移除范围,因为它导致生成器跳过10的倍数。

为了回到游泳池:

(defn return-sid [sid]
  (swap! sid-pool (fn [{:keys [cnt [_ & ids]]}]
                    {:cnt cnt
                     :sids (list* _ sid ids)})))

也许我对你想要做的事情感到困惑,但是在Clojure中创建唯一ID的规范方法可能仅仅是:

(let [counter (atom 0)]
  (defn get-unique-id []
    (swap! counter inc)))

不需要任何复杂的锁定。 注意:

  • 闭包封装了let-bound原子,因此您可以确定没有其他人可以触摸它。
  • swap! 操作可确保并发情况下的原子安全性,因此可以在不同线程之间共享get-unique-id函数。
  • 链接地址: http://www.djcxy.com/p/81451.html

    上一篇: Way to synchronize reads and writes in Clojure?

    下一篇: Removing the contents of a Chan or MVar in a single discrete step