2015-02-11 92 views
2

在SQL中,复制事务死锁是相对容易的。是否有可能在Clojure中复制事务死锁?

==SESSION1== 
begin tran 
update table1 set ... where ... 
[hold off further action - begin on next session] 

==SESSION2== 
begin 
update table1 set ... where ... 
[hold off further action - begin on next session] 

==SESSION3== 
<list blocked transactions - see session2> 
使用Clojure交易

现在 - 你不能只是打开他们,让他们打开时,s表达式不要让你做到这一点。

所以我很好奇上面的情况。

我的问题是:是否有可能在Clojure中复制事务死锁?

+0

死锁不会发生。活锁可以。在任何情况下,你必须产生两个线程并使用同步机制来实施一个场景。 – cgrand 2015-02-11 17:45:23

回答

0

Clojure中的STM旨在提供参考原子,一致性和隔离操作,而不锁定。为实现此目的,需要实现若干功能,如refs documentation中所述,但其中一个要点是要有一个“乐观”策略,该策略处理每个事务的数据版本,并在写入时比较该版本和参考版本。

这种乐观策略也可以在数据库中实现,例如,在Oracle

总之,Clojure中,如果你真的想创建一个僵局,你将不得不使用低级别mecanism,比如locking宏,明确创建一个对象(相同的Java​​)上的锁,并明确管理对共享资源的访问。

编辑:活锁 此示例来自Clojure的编程,@cgrand和人的实施例。

(let [retry-count (agent 0) 
     x (ref 0)] 
    (try 
    (dosync ;; transaction A 
     @(future (dosync ;; transaction B 
       (send-off retry-count inc) 
       (ref-set x 1))) 
     (ref-set x 2)) 
    (catch Exception e (println (str "caught exception: " (.getMessage e)))) 
    (finally 
    (await retry-count))) 
    [@x @retry-count]) 

caught exception: Transaction failed after reaching retry limit 
[1 10000] 
user> 

事务A在repl线程中执行。事务B将在单独的线程中执行,但由于future在A中被拒绝,它会阻塞,直到B完成。当A尝试(ref-set x 2)时,x已经被B修改过,并且这触发A的重试,这产生了新的线程和B事务...直到达到最大重试次数并引发异常。

+0

这很有趣。我可以在我的第一个Clojure事务(将来运行)中间做一个(Thread/sleep 1000)并阻止第二次Clojure事务吗? (也就是说锁宏实际上并不是必需的?) – hawkeye 2015-02-12 06:42:51

+1

实际上,第二个事务不会真的被阻塞,但它会重试,直到第一个事务完成或重试的限制达到(某些硬编码常量:10000)。我将用一个例子编辑我的答案。 – 2015-02-12 16:48:20

+0

@ T.Gounelle在什么时候事务A会意识到'x'的值被另一个事务改变了?我的意思是在需要时事务A得到x的'ref'? – lapots 2017-03-05 13:11:42

0

根据丰富的希基:

Clojure的STM和代理机制是无死锁。它们不是阻止选择性接收的消息传递系统。 STM在内部使用锁定,但会自动执行锁定冲突检测和解决。

更多详细资料请参阅this群。