kotlin協(xié)程2:協(xié)程間數(shù)據(jù)同步(多線程間協(xié)程的數(shù)據(jù)同步)

  1. 協(xié)程間的數(shù)據(jù)傳遞:單值傳遞和多值傳遞(數(shù)據(jù)流的傳遞)
    • 單值傳遞:
      • launch創(chuàng)建的協(xié)程:
        • 多協(xié)程是順序執(zhí)行瓦宜,且各個協(xié)程的函數(shù)體內(nèi)沒有掛起當前協(xié)程的邏輯(包括延時邏輯),各個協(xié)程的執(zhí)行順序是順序執(zhí)行,則下一個協(xié)程可以使用上一個協(xié)程的返回值灯帮。
        • 根協(xié)程聲明值瑞妇,子協(xié)程修改值玛荞,或者延遲產(chǎn)生數(shù)據(jù)實現(xiàn)在協(xié)程之間傳遞值始腾,不過需要注意的是分析根協(xié)程和各個協(xié)程的邏輯的執(zhí)行順序,對數(shù)據(jù)的修改順序會影響到數(shù)據(jù)的狀態(tài)進而也就影響了協(xié)程對數(shù)據(jù)的使用空执。
      • async創(chuàng)建的協(xié)程:
        • 協(xié)程并發(fā)浪箭,此時不建議協(xié)程間數(shù)據(jù)傳遞使用,個人理解的此時的使用場景是協(xié)程創(chuàng)建自己的數(shù)據(jù)而所有協(xié)程執(zhí)行完成匯總協(xié)程產(chǎn)生的數(shù)據(jù)進行使用辨绊,使用其并發(fā)縮短了邏輯的執(zhí)行時間奶栖。
      • 數(shù)據(jù)流的傳遞:
        • 通過通道(channel)進行協(xié)程間數(shù)據(jù)流的傳遞。
          • 阻塞隊列(BlockingQueue):項目中常見的模型(生產(chǎn)者消費者模型)常用的數(shù)據(jù)結(jié)構(gòu)门坷,提供put和take且都是阻塞的宣鄙,即take的時候若沒有數(shù)據(jù)則會阻塞線程直到有了數(shù)據(jù),同樣put也是沒有空間則阻塞線程等到數(shù)據(jù)take隊列有了空間默蚌。
          • 協(xié)程中的通道等同于阻塞隊列冻晤,用于協(xié)程間數(shù)據(jù)流的傳遞,提供send(數(shù)據(jù)的發(fā)送)和receive(數(shù)據(jù)的接受)和隊列不同的是不會阻塞绸吸。


            通道數(shù)據(jù)傳遞
            • 和隊列不同鼻弧,通道提供了close關(guān)閉邏輯,即通道關(guān)閉后則不會再有數(shù)據(jù)發(fā)送锦茁,close操作類似于向通道發(fā)送一個特殊的關(guān)閉標記. 收到這個關(guān)閉標記之后, 對通道的迭代操作將會立即停止, 因此可以保證在關(guān)閉操作以前發(fā)送的所有數(shù)據(jù)都會被正確接收攘轩。


              通道關(guān)閉
            • 構(gòu)造通道的生產(chǎn)者(producer):在協(xié)程中產(chǎn)生一個數(shù)值序列, 這是很常見的模式. 這是并發(fā)代碼中經(jīng)常出現(xiàn)的 生產(chǎn)者(producer)/消費者(consumer) 模式的一部分. 你可以將生產(chǎn)者抽象為一個函數(shù), 并將通道作為函數(shù)的參數(shù), 然后向通道發(fā)送你生產(chǎn)出來的值, 但這就違反了通常的函數(shù)設計原則, 也就是函數(shù)的結(jié)果應該以返回值的形式對外提供.
              通道Produce
              備注:如圖所示,produce是CoroutineScope提供的一個擴展函數(shù)码俩,參數(shù)為上下文度帮,通道緩沖區(qū)大小和產(chǎn)生序列數(shù)的lambda表達式,封裝了通道的創(chuàng)建及其上下文環(huán)境稿存,可以讓用戶專注于生產(chǎn)數(shù)序列即可笨篷。
              有一個便利的協(xié)程構(gòu)建器, 名為 produce, 它可以很簡單地編寫出生產(chǎn)者端的正確代碼, 還有一個擴展函數(shù) consumeEach, 可以在消費者端代碼中替代 for 循環(huán):即:
              通道produce封裝及其函數(shù)consumeEach
              • 帶緩沖區(qū)的通道:
                • 通道分為發(fā)送和接受兩個場景瞳秽,通常的通道如果沒有接受僅有發(fā)送的場景則是將發(fā)送協(xié)程掛起,等待接受協(xié)程的調(diào)用時才會執(zhí)行發(fā)送的邏輯進行數(shù)據(jù)的發(fā)送冕屯,同樣 先執(zhí)行接受協(xié)程也會將接受協(xié)程掛起等待發(fā)送協(xié)程的調(diào)用然后重新執(zhí)行接受協(xié)程寂诱。
              • 但是對于帶緩沖區(qū)的通道則是在數(shù)據(jù)的發(fā)送提供一個緩沖區(qū),即通道的參數(shù)指定發(fā)送數(shù)據(jù)的緩存?zhèn)€數(shù)安聘,此時通道沒有接受的場景痰洒,在僅有的發(fā)送場景的時候也會發(fā)送緩沖區(qū)的數(shù)據(jù),達到緩沖區(qū)的指定數(shù)后將發(fā)送數(shù)據(jù)協(xié)程掛起浴韭。
              • Channel() 工廠函數(shù)和 produce 構(gòu)建器都可以接受一個可選的 capacity 參數(shù), 用來指定 緩沖區(qū)大小. 緩沖區(qū)可以允許發(fā)送者在掛起之前發(fā)送多個數(shù)據(jù), 類似于指定了容量的 BlockingQueue, 它會在緩沖區(qū)已滿的時候發(fā)生阻塞.
                緩沖區(qū)通道
            • 定時器通道:類似于timer定時任務丘喻,即根據(jù)指定的定時時間發(fā)送一個unit的定時值,用戶可以根據(jù)定時值處理一些常見的定時邏輯念颈,可以使用 ticker 工廠函數(shù)來創(chuàng)建這種通道. 使用通道的 ReceiveChannel.cancel 方法來指出不再需要它繼續(xù)產(chǎn)生數(shù)據(jù)了泉粉。即:


              定時器通道
      • 多協(xié)程訪問通道:
        • 多協(xié)程接受通道的數(shù)據(jù):
          • 通道里的數(shù)據(jù)可以被多個協(xié)程收到,通道的數(shù)據(jù)的發(fā)送不會發(fā)生變化榴芳,接受數(shù)據(jù)的協(xié)程不固定
          • 取消通道的生成者協(xié)程嗡靡,則通道會被關(guān)閉,進而接受通道數(shù)據(jù)的協(xié)程也都會被取消窟感。
          • 在接受通道數(shù)據(jù)的協(xié)程的函數(shù)體中for循環(huán)和consumeEach的不同點是:前面for循環(huán)若出現(xiàn)異常并不會取消接受數(shù)據(jù)的協(xié)程因為豈不是掛起函數(shù)讨彼,也就不會取消其他的接受通道數(shù)據(jù)的協(xié)程,但是后者是一個掛起函數(shù)若出現(xiàn)異常則會取消協(xié)程柿祈,進而影響到通道的關(guān)閉哈误,其他接受者協(xié)程和生產(chǎn)者協(xié)程也就取消。


            生產(chǎn)者協(xié)程

            處理數(shù)據(jù)協(xié)程

            啟動多個數(shù)據(jù)的協(xié)程
        • 多協(xié)程向通道發(fā)送數(shù)據(jù):
          • 發(fā)射數(shù)據(jù)也可以多個協(xié)程向其寫入數(shù)據(jù)躏嚎,數(shù)據(jù)的順序取決于協(xié)程的寫入邏輯蜜自。
          • 取消所有的協(xié)程則通道的數(shù)據(jù)也就隨之取消。


            通道數(shù)據(jù)生產(chǎn)者

            多協(xié)程寫入數(shù)據(jù)
        • 如果從多個協(xié)程中調(diào)用通道的發(fā)送和接收操作, 從調(diào)用發(fā)生的順序來看, 這些操作是 平等的. 通道對這些方法以先進先出(first-in first-out)的順序進行服務, 也就是說, 第一個調(diào)用 receive 的協(xié)程會得到通道中的數(shù)據(jù). 在下面的示例程序中, 有兩個 "ping" 和 "pong" 協(xié)程, 從公用的一個 "table" 通道接收 "ball" 對象.


          多協(xié)程訪問通道的順序
      • 管道:上面講了多協(xié)程生產(chǎn)數(shù)據(jù)和多協(xié)程接收數(shù)據(jù)卢佣,在這個場景中可以講這些協(xié)程串聯(lián)起來形成一個數(shù)據(jù)處理的管道重荠。
      • ReceiveChannel:通道的數(shù)據(jù)接受的封裝對象,producer產(chǎn)生數(shù)據(jù)后返回的對象即時這個對象虚茶,封裝了產(chǎn)生的數(shù)據(jù)(具體可以參考上面的代碼)晚缩。
      • 管道的協(xié)程串聯(lián)即是通過上面的這個對象進行串聯(lián),即中間協(xié)程可以接受生產(chǎn)者產(chǎn)生的數(shù)據(jù)修改后再封裝返回媳危,最終到最后一個協(xié)程通過其接受者獲取到數(shù)據(jù)荞彼。即:


        管道生產(chǎn)者

        管道數(shù)據(jù)中間處理

        管道的串聯(lián)
  2. 多線程協(xié)程的數(shù)據(jù)的同步:
    問題:
    • 通過Default線程指定,多協(xié)程的執(zhí)行會在多線程中執(zhí)行待笑,此時就會出現(xiàn)多線程共享值的問題鸣皂,即多個線程同時訪問并修改同一個值就會出現(xiàn)意想不到的問題。


      共享值

      多線程

      備注:多線程中多協(xié)程的原因?qū)е虏]有出現(xiàn)100*1000的結(jié)果值。

  • 多協(xié)程的多線程執(zhí)行的解決和多線程共享值基本一樣寞缝,即:
    • 針對簡單的基礎變量癌压,使用volatile(即使用其原子性:針對變量的每一個線程的讀和寫保證其可見性)不能保證并發(fā)問題解決,對于上面的案例使用這個關(guān)鍵字并不能保證每次都是100*1000荆陆,即:


      并發(fā)案例
    • 下面幾種方法可以保證多線程對共享值的修改的線程安全滩届。
      • 和多線程處理一致數(shù)據(jù)結(jié)構(gòu)使用線程安全的數(shù)據(jù)結(jié)構(gòu),即使用線程安全的 (也叫 同步的(synchronized), 線性的(linearizable), 或者 原子化的(atomic)) 數(shù)據(jù)結(jié)構(gòu), 這些數(shù)據(jù)結(jié)構(gòu)會對需要在共享的狀態(tài)數(shù)據(jù)上進行的操作提供必要的同步保障. 在我們的簡單的計數(shù)器示例中, 可以使用 AtomicInteger 類, 它有一個原子化的 incrementAndGet 操作且對于這個具體的問題, 這是最快的解決方案. 這種方案適用于計數(shù)器, 集合, 隊列, 以及其他標準數(shù)據(jù)結(jié)構(gòu), 以及這些數(shù)據(jù)結(jié)構(gòu)的基本操作. 但是, 這種方案并不能簡單地應用于復雜的狀態(tài)變量, 或者那些沒有現(xiàn)成的線程安全實現(xiàn)的復雜操作.


        線程安全的數(shù)據(jù)結(jié)構(gòu)
      • 細粒度的線程限定:將涉及到的共享值轉(zhuǎn)變的lambda表達式或者匿名函數(shù)放到獨一線程中去被啼,即對于共享值的修改放到唯一線程中去修改帜消。
        • 缺點是:代碼的執(zhí)行變慢了,因為在執(zhí)行中要不停的進行線程的切換浓体,即協(xié)程線程和修改數(shù)據(jù)的線程來回切換泡挺。


          單線程
        • 粗粒度的線程限定:即將整個操作放到一個獨立線程中去這樣比細粒度線程限定速度快又能保證共享值的線程安全。


          單線程線程安全
          • 加鎖同步即和多線程中的鎖同步一致命浴,不過協(xié)程的語法和線程不一致娄猫,協(xié)程是通過Mutex實現(xiàn)的它的 lock和 unlock 函數(shù)可以用來界定臨界區(qū). 主要的區(qū)別在于 Mutex.lock() 是一個掛起函數(shù). 它不會阻塞線程.
            * 還有一個擴展函數(shù) withLock, 它用非常便利的方式實現(xiàn) mutex.lock(); try { ... } finally { mutex.unlock() } 模式:
            加鎖實現(xiàn)同步
  1. select選擇:上面通道中介紹了多個掛起函數(shù)發(fā)送值的場景,使用select選擇語法可以在多個掛起函數(shù)中選擇第一個執(zhí)行完畢的結(jié)果生闲,其他掛起函數(shù)取消或者關(guān)閉媳溺。
    • 通道中選擇使用:


      掛起函數(shù)1

      掛起函數(shù)2

      通道選擇掛起函數(shù)

      測試代碼及其結(jié)果
    • 通道的關(guān)閉會導致通道的select選擇的onReceive函數(shù)語句失敗且拋出對應的異常,對于此可以使用函數(shù)onReceiveCatching對其進行抓取并處理其異常碍讯。即:


      選擇處理異常掛起函數(shù)

      生產(chǎn)者掛起函數(shù)及其測試掛起函數(shù)和結(jié)果

      備注:通道上選擇的優(yōu)先級:

      • 多個通道且每個通道的數(shù)據(jù)發(fā)送有先后順序:此時取決于數(shù)據(jù)的發(fā)送順序褂删,例如上面的第一個案例,第一個通道500ms發(fā)送一個數(shù)據(jù)所以第一個數(shù)據(jù)是第一個通道冲茸,且第一個通道的數(shù)據(jù)居多,偶爾第二個通道也會有數(shù)據(jù)缅帘。
      • 多個通道且每個通道沒有具體的數(shù)據(jù)發(fā)送順序區(qū)分:比如第二個案例:優(yōu)先使用第一個通道轴术,后續(xù)會根據(jù)通道的數(shù)據(jù)發(fā)送順序獲取數(shù)據(jù)。
    • 通道的數(shù)據(jù)發(fā)送時選擇發(fā)送的通道:上面介紹了在接受數(shù)據(jù)的時候多通道的選擇钦无,此處介紹的是發(fā)送數(shù)據(jù)發(fā)送到多個通道:選擇表達式也可以使用 onSend 子句, 它可以與選擇表達式的偏向性結(jié)合起來, 起到很好的作用逗栽。

參考文章:
協(xié)程間的通信
協(xié)程與線程間的關(guān)系
協(xié)程的并發(fā)問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市弟塞,隨后出現(xiàn)的幾起案子凭峡,更是在濱河造成了極大的恐慌,老刑警劉巖决记,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摧冀,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機索昂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門建车,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人椒惨,你說我怎么就攤上這事缤至。” “怎么了康谆?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵领斥,是天一觀的道長。 經(jīng)常有香客問我秉宿,道長戒突,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任描睦,我火速辦了婚禮膊存,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘忱叭。我一直安慰自己隔崎,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布韵丑。 她就那樣靜靜地躺著爵卒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撵彻。 梳的紋絲不亂的頭發(fā)上钓株,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音陌僵,去河邊找鬼轴合。 笑死,一個胖子當著我的面吹牛碗短,可吹牛的內(nèi)容都是我干的受葛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼偎谁,長吁一口氣:“原來是場噩夢啊……” “哼总滩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起巡雨,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤闰渔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铐望,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澜建,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡向挖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了炕舵。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片何之。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咽筋,靈堂內(nèi)的尸體忽然破棺而出溶推,到底是詐尸還是另有隱情,我是刑警寧澤奸攻,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布蒜危,位于F島的核電站,受9級特大地震影響睹耐,放射性物質(zhì)發(fā)生泄漏辐赞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一硝训、第九天 我趴在偏房一處隱蔽的房頂上張望响委。 院中可真熱鬧,春花似錦窖梁、人聲如沸赘风。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邀窃。三九已至,卻和暖如春假哎,著一層夾襖步出監(jiān)牢的瞬間瞬捕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工舵抹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肪虎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓掏父,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秆剪。 傳聞我的和親對象是個殘疾皇子赊淑,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容