Clojure锁定字符串值

时间:2018-12-11 16:31:07

标签: concurrency clojure

我在clojure中有一段代码,应该在隔离状态下运行。可以说这个函数是

(defn isolate [string1])

很容易在所有输入上隔离整个函数,就像这样调用它:

(def o (Object. ))

(locking o (isolate string1))

但是,这仅允许一个进程/线程同时访问隔离。

我现在实现的是以下内容:

(def current-locks (ref {}))

(defn mergeReverse [x y] (merge y x))

(defn merge-with-current-locks [key val]
  (dosync (alter current-locks mergeReverse {key val})))

(defn remove-lock [key]
  (dosync (alter current-locks dissoc key)))

最后是线程阻塞调用此方法

(defn block-until-free [key val]
  (let [_ (merge-with-current-locks key val)]
    (if (dosync (and (contains? current-locks key)
                     (not= (get current-locks key) val)))
      (do
        (Thread/sleep 10)
        (block-until-free key val)))))

您可以在解决方案中看到,我在这里使用了键和值,尽管我只锁定键,但是能够使用映射而不是数组是有好处的,因为我使用了仅在map合并时才在映射中合并的merge属性不包含此值,并且由于current-locksref,因此我使用了alter并交换了合并输入以获取所需的行为。

据我所知(我已经对其进行测试),此hack有效。但是我的问题是如何以正确的Clojure方法执行此操作?这个解决方案似乎很复杂

当然,一旦执行 critical 函数,就必须调用remove-lock

1 个答案:

答案 0 :(得分:2)

您应该为此使用数据库事务。这是Clojure代码的示例:

  ; Wraps all commands in a single transaction
  (jdbc/with-db-transaction
    [tx db-conn]
    (let [clj-id (grab :id (only (jdbc/query tx ["select id from langs where lang='Clojure'"])))]
      (jdbc/insert-multi! tx :releases
                          [{:desc "ancients" :langId clj-id}
                           {:desc "1.8" :langId clj-id}
                           {:desc "1.9" :langId clj-id}]))
    (let [java-id (grab :id (only (jdbc/query tx ["select id from langs where lang='Java'"])))]
      (jdbc/insert-multi! tx :releases
                          [{:desc "dusty" :langId java-id}
                           {:desc "8" :langId java-id}
                           {:desc "9" :langId java-id}
                           {:desc "10" :langId java-id}])))

我们在表langs中查询语言idClojure的{​​{1}}值。然后,我们将列Java和外键releases添加到表desc中。因为两个langId语句都是通过insert-multi!包装的,所以如果有任何其他线程在完成之前更新了数据库,则事务将回滚。

如果我们的交易失败,上面的代码将需要一个重试循环来捕获异常,然后重试(可能会有随机延迟)。您可以找到the entire sample code here


更新

我的示例用于SQL数据库,例如Postgres。对于Datomic,我相信您将需要诸如(jdbc/with-db-transaction之类的函数。 See the Datomic docs了解更多详细信息。您还可以问on the Datomic mailing list或在StackOverflow上发布更具体的Datomic问题。

对于Postgres或Datomic,仅当您更改的特定行/实体也被另一个线程更改时,事务才会中止。它不会锁定整个数据库。