<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang 協(xié)程Goroutine到底是怎么回事?(二)

          共 2453字,需瀏覽 5分鐘

           ·

          2020-07-11 12:16

          ?協(xié)程不僅只有調(diào)度,還需要配套的齊全設(shè)備,比如協(xié)程鎖,定時器,條件變量等



          上一篇從協(xié)程的通用原理講起,講了通Golang的協(xié)程,使用一個完成的協(xié)程,必須要配合完善的配套設(shè)備,協(xié)程鎖,定時器等,這文章就是描述于此。




          Go 協(xié)程配套設(shè)備



          Golang 協(xié)程鎖,定時器,是怎么回事?系統(tǒng)調(diào)用又有什么特殊,G-M鎖定是什么?


          協(xié)程鎖


          之前提到,協(xié)程使用之后,是必須配套實現(xiàn)一些配件的。關(guān)鍵就是要保證在執(zhí)行g(shù)oroutine的時候不阻塞。最典型的的就是鎖、timer、系統(tǒng)調(diào)用這三個方面。其中鎖必須要是協(xié)程鎖。

          舉例:某個場景,任務(wù)A需要修改Z,任務(wù)B也需要修改Z。如果是串行系統(tǒng),A執(zhí)行完了,再執(zhí)行B,那么不會有問題。A -> B ?,F(xiàn)在A,B是goroutine,可以并發(fā)執(zhí)行,那么在操作Z的時候我們必須要有保證串行化的機制。

          CO_LOCK{    #處理邏輯}CO_UNLOCK

          現(xiàn)在的關(guān)鍵點就是,我們不能直接用之前的mutex鎖,或者是自旋鎖。這樣會嚴重影響并發(fā),或者導(dǎo)致死鎖。而必須配套實現(xiàn)協(xié)程鎖。

          sync.Mutex.Lock -> runtime_SemacquireMutex    -> sync_runtime_SemacquireMutex        -> semacquire1 // runtime/sema.go
          1. 當加鎖失敗,則保存上下文,把自己賦值到一個sudog結(jié)構(gòu)里

          2. 掛接到鎖內(nèi)部相關(guān)隊列里(semaRoot),root.queue() 。

          3. 調(diào)用goparkunlock主動切走,切到調(diào)度協(xié)程

          sync.Mutex.Unlock-> runtime_Semrelease    -> sync_runtime_Semrelease        -> semrelease1
          1. 解鎖

          2. 取出這個鎖內(nèi)部等待隊列的一個元素(g)

          3. 調(diào)用goready喚醒goroutine,投入隊列中,等待執(zhí)行?


          dc47a68da5b0d03400b6ce6ae0b0acad.webp


          現(xiàn)在就以A, B任務(wù)同時處理Z來舉例:

          1. A因為要修改Z,所以加了協(xié)程鎖

          2. 加鎖之后,由于處理一些其他的邏輯,因為某些等待事件,又把cpu切到M.g0調(diào)度了 (yield);注意了還沒有放鎖

          3. 這個時候M把B拿過來執(zhí)行,yield to B

          4. B也要修改Z,這個時候發(fā)現(xiàn)鎖已經(jīng)被加上了,于是把自己掛到鎖結(jié)構(gòu)里面去

          5. 然后B直接切走,yield to M.g0

          6. 現(xiàn)在A的事件滿足了,M.g0 重新調(diào)度到A執(zhí)行,yield to A

          7. A 從剛剛切走的地方開始執(zhí)行,然后放鎖

            1. 注意了,放鎖這里就會把B這個協(xié)程任務(wù)從鎖隊列中摘除,加到調(diào)度隊列中,

          8. A執(zhí)行完成之后,M.g0 調(diào)度B執(zhí)行

          9. B從剛剛加鎖的地方喚醒,于是加上鎖了。然后走鎖內(nèi)邏輯,走完就放鎖


          以上就是協(xié)程鎖的實現(xiàn)原理。保證A,B在修改Z的時候必須串行化。(旁白:加鎖其實就是入隊,串行入隊,解鎖就是出隊,串行出隊喚醒)


          timer


          time的實現(xiàn)原理:

          1. time.Sleep()的時候先創(chuàng)建好timer結(jié)構(gòu)體,掛到哈希表

          2. 確保創(chuàng)建了一個goroutine(timeproc),這個會不斷檢查超時的timer

          3. 調(diào)用gopark保存棧,切到調(diào)度

          4. timeproc循環(huán)檢查,當發(fā)現(xiàn)有超時的timer的時候,調(diào)用goready,把這個掛到運行隊列里,等待運行


          系統(tǒng)調(diào)用


          對于某些系統(tǒng)調(diào)用,可能是會導(dǎo)致阻塞的,所以這個也必須封裝才能讓goroutine有讓出cpu的機會。go內(nèi)部實現(xiàn)系統(tǒng)調(diào)用會在前后包裝兩個函數(shù):

          entersyscallexitsyscall

          解決syscall可能導(dǎo)致的問題關(guān)鍵就在這兩個函數(shù)。這兩個函數(shù)主要做了這些事情

          entersyscall

          1. 設(shè)置p的狀態(tài)為 _Psyscall

          2. 暫時解除P->M的綁定。但是M是有路徑找到P的。并且雖然解除了P->M的綁定,但是這里并不會把P綁定到其他的M

          exitsyscall

          1. 先嘗試綁定到之前P

          2. 如果之前的P已經(jīng)被sysmon處理掉了,那么則挑選一個空閑的P

          3. 如果還不行,則掛到全局隊列sched里面去


          (旁白:封裝這兩個函數(shù),就是為了監(jiān)控,不能讓這一個系統(tǒng)調(diào)用阻塞了隊列里所有的任務(wù)。你不能執(zhí)行P了,就讓給別人,就是這個思路)


          sysmon線程就是處理_Psyscall狀態(tài)的P,發(fā)現(xiàn)有超時的,則把P找個空閑的綁定,去執(zhí)行P隊列里的協(xié)程任務(wù)。?


          G-M鎖定

          golang支持了一個G-M鎖定的功能,通過lockOSThread和unlockOSThread來實現(xiàn)。主要是用于一些cgo調(diào)用,或者一些特殊的庫,有些庫是要求固定在一個線程上跑。


          1. G_a鎖定M0 lockOSThread

          2. G_a調(diào)用gosched切走,投入P1隊列

          3. M0調(diào)度,發(fā)現(xiàn)是lockedm,于是讓出P0,自己調(diào)用notesleep睡眠

          4. M1取出G_a,發(fā)現(xiàn)是lockedg,于是讓出P1給M0,并且喚醒M0.?自己變idle,stopm休眠

          5. M0繼續(xù)執(zhí)行G_a


          你可以發(fā)現(xiàn),G_a只在M0上運行,鎖定這段期間,M0也只執(zhí)行了G_a任務(wù)。?


          當前go有哪些問題


          當前go沒有實現(xiàn)異步io。換句話說,如果在一個goroutine里面使用read/write io的系統(tǒng)調(diào)用,這些都是同步的io調(diào)用。會實實在在的阻塞M的調(diào)度,在遇到io延遲慢的時候,會導(dǎo)致sysmon檢查到M-P超時(10ms),那么就會把M-P解綁,M游離出去執(zhí)行阻塞任務(wù),分配一個新的M來綁定P執(zhí)行隊列里的任務(wù)。


          那么這種情況,雖然沒有完全阻塞死P任務(wù)的執(zhí)行,但是代價非常大,而且可能會導(dǎo)致M的數(shù)量一直飆升。就算沒有這些極限情況,IO的并發(fā)能力相較于aio也是不行的。(旁白:Golang能切走的當前只有網(wǎng)絡(luò)IO,磁盤io走的是系統(tǒng)調(diào)用,協(xié)程切不走)


          當前net庫是已經(jīng)實現(xiàn)了底層的patch,aio還沒有實現(xiàn)關(guān)鍵還是aio的復(fù)雜性導(dǎo)致的。?其實很多的工程實踐是通過libaio來實現(xiàn)磁盤io的異步,配合協(xié)程一起使用。



          a61b30224073083506dd35a36fcc0877.webp








          推薦閱讀





          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产黄色视频在线看 | AV天堂资源成人 | MD-0127 艾秋 分享骚货老婆 强势调教处男晚辈 | aⅴ黄色电影 | 18成人片黄网站www |