<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    Clojure的并發(fā)(二)Write Skew分析

    Posted on 2010-07-17 05:44 dennis 閱讀(4959) 評論(1)  編輯  收藏 所屬分類: Clojure
    Clojure 的并發(fā)(一) Ref和STM
    Clojure 的并發(fā)(二)Write Skew分析
    Clojure 的并發(fā)(三)Atom、緩存和性能
    Clojure 的并發(fā)(四)Agent深入分析和Actor
    Clojure 的并發(fā)(五)binding和let
    Clojure的并發(fā)(六)Agent可以改進(jìn)的地方
    Clojure的并發(fā)(七)pmap、pvalues和pcalls
    Clojure的并發(fā)(八)future、promise和線程

         在介紹Ref的上一篇blog提到,基于snapshot做隔離的MVCC實(shí)現(xiàn)來說,有個現(xiàn)象,叫寫偏序——Write Skew。根本的原因是由于每個事務(wù)在更新過程中無法看到其他事務(wù)的更改的結(jié)果,導(dǎo)致各個事務(wù)提交之后的最終結(jié)果違反了一致性。為了理解這個現(xiàn)象,最好的辦法是在代碼中復(fù)現(xiàn)這個現(xiàn)象。考慮下列這個場景:
       屁民Peter有兩個賬戶account1和account2,簡稱為A1和A2,這兩個賬戶各有100塊錢,一個顯然的約束就是這兩個賬戶的余額之和必須大于或者等于零,銀行肯定不能讓你賺了去,你也怕成為下個許霆。現(xiàn)在,假設(shè)有兩個事務(wù)T1和T2,T1從A1提取200塊錢,T2則從A2提取200塊錢。如果這兩個事務(wù)按照先后順序進(jìn)行,后面執(zhí)行的事務(wù)判斷A1+A2-200>=0約束的時候發(fā)現(xiàn)失敗,那么就不會執(zhí)行,保證了一致性和隔離性。但是基于多版本并發(fā)控制的Clojure,這兩個事務(wù)完全可能并發(fā)地執(zhí)行,因?yàn)樗麄兌际腔谝粋€當(dāng)前賬戶的快照做更新的, 并且在更新過程中無法看到對方的修改結(jié)果,T1執(zhí)行的時候判斷A1+A2-200>=0約束成立,從A1扣除了200塊;同樣,T2查看當(dāng)前快照也滿足約束A1+A2-200>=0,從A2扣除了200塊,問題來了,最終的結(jié)果是A1和A2都成-100塊了,身為屁民的你竟然從銀行多拿了200塊,你等著無期吧。

       現(xiàn)在,我們就來模擬這個現(xiàn)象,定義兩個賬戶:

    ;;兩個賬戶,約束是兩個賬戶的余額之和必須>=0
    (def account1 (
    ref 100))
    (def account2 (
    ref 100))

       定義一個取錢方法:
    ;;定義扣除函數(shù)
    (defn deduct [account n other]
          (dosync 
              (
    if (>= (+ (- @account n) @other0)
                  (alter account 
    - n))))

       其中account是將要扣錢的帳號,other是peter的另一個帳號,在執(zhí)行扣除前要滿足約束@account-n+@other>=0

       接下來就是搞測試了,各啟動N個線程嘗試從A1和A2扣錢,為了盡快模擬出問題,使得并發(fā)程度高一些,我們將線程設(shè)置大一些,并且使用java.util.concurrent.CyclicBarrier做關(guān)卡,測試代碼如下:

    ;;設(shè)定關(guān)卡
    (def barrier (java
    .util.concurrent.CyclicBarrier. 6001))
    ;;各啟動3000個線程嘗試去從賬戶1和賬戶2扣除200
    (dotimes [_ 
    3000] (.start (Thread. #(do (.await  barrier) (deduct account1 200 account2) (.await  barrier)))))
    (dotimes [_ 3000] (.start (Thread. #(do (.await  barrier) (deduct account2 200 account1) (.await  barrier)))))

    (
    .await barrier)

    (
    .await barrier)
    ;;打印最終結(jié)果
    (println 
    @account1)
    (println 
    @account2)

         線程里干了三件事情:首先調(diào)用barrier.await嘗試突破關(guān)卡,所有線程啟動后沖破關(guān)卡,進(jìn)入扣錢環(huán)節(jié)deduct,最后再調(diào)用barrier.await用于等待所有線程結(jié)束。在所有線程結(jié)束后,打印當(dāng)前賬戶的余額。

         這段代碼在我的機(jī)器上每執(zhí)行10次左右都至少有一次打印:
    -100
    -100
       
        這表示A1和A2的賬戶都欠下了100塊錢,完全違反了約束條件,法庭的傳票在召喚peter。

        那么怎么防止write skew現(xiàn)象呢?如果我們能在事務(wù)過程中保護(hù)某些Ref不被其他事務(wù)修改,那么就可以保證當(dāng)前的snapshot的一致性,最終保證結(jié)果的一致性。通過ensure函數(shù)即可保護(hù)Ref,稍微修改下deduct函數(shù):
    (defn deduct [account n other]
          (dosync (ensure account) (ensure other)
              (
    if (>= (+ (- @account n) @other0)
                  (alter account 
    - n))))

       在執(zhí)行事務(wù)更新前,先通過ensure保護(hù)下account和other賬戶不被其他事務(wù)修改。你可以再多次運(yùn)行看看,會不會再次打印非法結(jié)果。

       上篇blog最后也提到了一個士兵巡邏的例子來介紹write skew,我也寫了段代碼來模擬那個例子,有興趣可以跑跑,非法結(jié)果是三個軍營的士兵之和小于100(兩個軍營最后只剩下25個人)。

    ;1號軍營
    (def g1 (
    ref 45))
    ;2號軍營
    (def g2 (
    ref 45))
    ;3號軍營
    (def g3 (
    ref 45))
    ;從1號軍營抽調(diào)士兵
    (defn dispatch
    -patrol-g1 [n]
        (dosync 
          (
    if (> (+ (- @g1 n) @g2 @g3100)
              (alter g1 
    - 20)
            ))
          )
    ;從2號軍營抽調(diào)士兵
    (defn dispatch
    -patrol-g2 [n]
        (dosync 
          (
    if (> (+ @g1 (- @g2 n) @g3100)
              (alter g2 
    - 20)
            ))
          )
    ;;設(shè)定關(guān)卡
    (def barrier (java
    .util.concurrent.CyclicBarrier. 4001))
    ;;各啟動2000個線程嘗試去從1號和2號軍營抽調(diào)20個士兵
    (dotimes [_ 
    2000] (.start (Thread. #(do (.await  barrier) (dispatch-patrol-g1 20) (.await  barrier)))))
    (dotimes [_ 2000] (.start (Thread. #(do (.await  barrier) (dispatch-patrol-g2 20) (.await  barrier)))))
    ;(dotimes [_ 10] (.start (Thread. #(do (.await  barrier) (dispatch-patrol-g3 20) (.await  barrier)))))

    (
    .await barrier)

    (
    .await barrier)
    ;;打印最終結(jié)果
    (println 
    @g1)
    (println 
    @g2)
    (println 
    @g3)





    評論

    # re: Clojure的并發(fā)(二)Write Skew分析  回復(fù)  更多評論   

    2014-10-30 22:56 by chen_767
    (defn deduct [account n other]
    (dosync (ensure account) (ensure other)
    (if (>= (+ (- @account n) @other) 0)
    (alter account - n))))

    中可以不ensure account吧?
    主站蜘蛛池模板: 国产大片免费网站不卡美女| 色屁屁在线观看视频免费| 人人鲁免费播放视频人人香蕉| 在线观看免费人成视频色9| 亚洲日韩国产精品无码av| 少妇人妻偷人精品免费视频| 久久久综合亚洲色一区二区三区| a在线免费观看视频| 亚洲人成网站在线观看播放| 97在线免费观看视频| 久久亚洲伊人中字综合精品| 三年片在线观看免费观看大全动漫 | 麻豆国产入口在线观看免费| 国产亚洲精aa在线看| 蜜臀91精品国产免费观看| 亚洲AV无码一区二区三区网址| 日日夜夜精品免费视频| 黄页网址在线免费观看| 亚洲区小说区图片区QVOD| 久久国产精品一区免费下载| 亚洲激情电影在线| 在线播放免费播放av片| 日韩毛片一区视频免费| 亚洲午夜久久久影院| 1000部免费啪啪十八未年禁止观看| 亚洲人成电影院在线观看| 国产18禁黄网站免费观看| 国产日韩AV免费无码一区二区| 亚洲国产成人久久精品app| 黑人粗长大战亚洲女2021国产精品成人免费视频 | 国产精品久久久亚洲| 最近最新高清免费中文字幕| 亚洲伊人久久大香线蕉AV| 免费中文字幕不卡视频| 无码A级毛片免费视频内谢| 亚洲乱码在线卡一卡二卡新区| 亚洲精品成人在线| 欧洲一级毛片免费| 黄床大片30分钟免费看| 久久久亚洲欧洲日产国码aⅴ| 免费无码成人AV片在线在线播放|