<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] > [參考](https://lessisbetter.site/2019/03/10/golang-scheduler-1-history/) ## 協程 (co-routine) - 進程切換需要消耗大量的資源 - 線程切換雖然資源少,但是會有鎖和沖突檢測 線程分為**內核態線程**和**用戶態線程**,**用戶態線程需要綁定內核態線程** CPU并不能感知用戶態線程的存在,它只知道它在運行1個線程,這個線程實際是內核態線程 用戶態線程實際有個名字叫協程(co-routine),為了容易區分,我們使用協程指用戶態線程,使用線程指內核態線程 ## 協程和線程有3種映射關系 * N:1,N個協程綁定1個線程,優點就是**協程在用戶態線程即完成切換,不會陷入到內核態,這種切換非常的輕量快速**。但也有很大的缺點,1個進程的所有協程都綁定在1個線程上,一是某個程序用不了硬件的多核加速能力,二是一旦某協程阻塞,造成線程阻塞,本進程的其他協程都無法執行了,根本就沒有并發的能力了。 * 1:1,1個協程綁定1個線程,這種最容易實現。協程的調度都由CPU完成了,不存在N:1缺點,但有一個缺點是協程的創建、刪除和切換的代價都由CPU完成,有點略顯昂貴了。 * M:N,M個協程綁定N個線程,是N:1和1:1類型的結合,克服了以上2種模型的缺點,但實現起來最為復雜 ## 協稱 (goroutine) - 它非常輕量,一個goroutine只占幾KB,可在有限的內存空間內支持大量goroutine - 雖然一個goroutine的棧只占幾KB,但實際是可伸縮的,如果需要更多內容,runtime會自動為goroutine分配 ## 老調度器 **調度器的任務是在用戶態完成goroutine的調度,而調度器的實現好壞,對并發實際有很大的影響,并且Go的調度器就是M:N類型的,實現起來也是最復雜** 老的調度,新的在2012年被使用 ![](https://lessisbetter.site/images/2019-03-old-scheduler.png) - runtime在Go中很重要,許多程序運行時的工作都由runtime完成 - 調度器就是runtime的一部分,虛線圈出來的為調度器,他分為: 1. M,代表線程,它要運行goroutine 2. Global G Queue,是全局goroutine隊列,所有的goroutine都保存在這個隊列中,goroutine用G進行代表 M想要執行、放回G都必須訪問全局G隊列,并且M有多個,即多線程訪問同一資源需要加鎖進行保證互斥/同步,所以全局G隊列是有互斥鎖進行保護的 老的調度器有4個缺點: 1. 創建、銷毀、調度G都需要每個M獲取鎖,這就形成了**激烈的鎖競爭**。 2. M轉移G會造成**延遲和額外的系統負載**。比如當G中包含創建新協程的時候,M創建了G’,為了繼續執行G,需要把G’交給M’執行,也造成了**很差的局部性**,因為G’和G是相關的,最好放在M上執行,而不是其他M’。 3. M中的mcache是用來存放小對象的,mcache和棧都和M關聯造成了大量的內存開銷和差的局部性。 4. 系統調用導致頻繁的線程阻塞和取消阻塞操作增加了系統開銷。 ## 新調度器 新調度器引入了以下概念: * **P**:**Processor,它包含了運行goroutine的資源**,如果線程想運行goroutine,必須先獲取P,P中還包含了可運行的G隊列,GOMAXPROCS設置P的數量 * work stealing:當M綁定的P沒有可運行的G時,它可以從其他運行的M’那里偷取G。 現在有了最熟悉的調度模型 GMP * **G**: goroutine * **M**: 工作線程 * **P**: 處理器,它包含了運行Go代碼的資源,M必須和一個P關聯才能運行G ## 調度器 ### 兩大思想 - **復用線程**:協程本身就是運行在一組線程之上,不需要頻繁的創建、銷毀線程,而是對線程的復用。在調度器中復用線程還有2個體現: 1. work stealing,當本線程無可運行的G時,嘗試從其他線程綁定的P偷取G,而不是銷毀線程。 2. hand off,當本線程因為G進行系統調用阻塞時,線程釋放綁定的P,把P轉移給其他空閑的線程執行。 - **利用并行**:GOMAXPROCS設置P的數量,當GOMAXPROCS大于1時,就最多有GOMAXPROCS個線程處于運行狀態,這些線程可能分布在多個CPU核上同時運行 ### 兩小策略 - **搶占**:在coroutine中要等待一個協程主動讓出CPU才執行下一個協程,在Go中,一個goroutine最多占用CPU 10ms,防止其他goroutine被餓死,這就是goroutine不同于coroutine的一個地方 - **全局G隊列**:在新的調度器中依然有全局G隊列,但功能已經被弱化了,當M執行work stealing從其他P偷不到G時,它可以從全局G隊列獲取G
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看