Concurrency Clojure对字符串值的锁定
我在clojure中有一段代码,应该在隔离状态下运行。假设这个函数是Concurrency Clojure对字符串值的锁定,concurrency,clojure,Concurrency,Clojure,我在clojure中有一段代码,应该在隔离状态下运行。假设这个函数是 (定义隔离[string1]) 很容易在所有输入上隔离整个函数,这样调用: (def o (Object. )) (locking o (isolate string1)) 但是,这只允许一个进程/线程同时访问 我现在实施的是以下内容: (def current-locks (ref {})) (defn mergeReverse [x y] (merge y x)) (defn merge-with-current-
(定义隔离[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)))))
正如您在解决方案中所看到的,我在这里使用了键和值,尽管我只锁定键,但能够使用映射而不是数组是有益的,因为我使用了merge属性,该属性仅在映射不包含此值时才会合并到映射中,并且因为当前锁
是ref
我使用了alter
和交换合并输入以获得所需的行为
据我所知,这个黑客程序是有效的(我已经测试过了)。但我的问题是,我如何才能以正确的方式做到这一点?这个解决方案似乎很复杂
当然,一旦执行了critical功能,就必须调用
移除锁 您应该为此使用数据库事务。以下是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
,查找id
语言Clojure
和Java
的值。然后,我们使用colsdesc
和外键langId
将行添加到表releases
。因为这两个都插入了多个代码>语句通过(jdbc/with db transaction
)包装,如果任何其他线程在完成之前更新了数据库,则事务将回滚
如果事务失败,上面的代码需要一个重试循环来捕获异常,然后重试(可能是随机延迟)
更新
我的例子是一个SQL数据库,比如Postgres。对于Datomic,我相信您需要一个函数,比如db.fn/cas
。有关详细信息,您还可以询问或发布有关StackOverflow的更具体的Datomic问题
对于Postgres或Datomic,只有当您更改的特定行/实体也被另一个线程更改时,事务才会中止。它不会锁定整个数据库。您能否在此示例中添加动机/用例?因为每个线程都有自己的局部变量副本,而且函数输入在Clojure中是不可变的,所以看起来每个线程都已完全隔离。如果您希望改变某个共享全局状态,则不会在示例中显示。对于我的数据库表中特定标记下的每个附加值,我需要调用一个服务并更新该值和该标记中的每个值。同时,其他进程不应尝试更新该标记。这就是为什么我想在我的服务仍在计算时阻止所有其他进程访问此关键功能?这通常不会同时发生,但当它发生时,它需要被阻止创建我理解您的解决方案,但当您说“如果任何其他线程更新数据库”,这让我觉得这是必要的。我们使用Datomic db,很多服务可能会更新db,如果发生这种情况,我不想回滚tx,因为这样就没有必要了。我只关心在出现具有相同标记的其他进程时回滚tx。此解决方案与使用锁定的a有什么区别我的理解是锁定会更好,因为它不会留下回滚选项。我正确理解你的方法吗?