亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go routine調度詳解

 更新時間:2019年01月07日 09:17:16   作者:曾紀文  
這篇文章主要介紹了Go routine調度詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

goroutine簡介

goroutine是go語言中最為NB的設計,也是其魅力所在,goroutine的本質是協程,是實現并行計算的核心。goroutine使用方式非常的簡單,只需使用go關鍵字即可啟動一個協程,并且它是處于異步方式運行,你不需要等它運行完成以后在執(zhí)行以后的代碼。

go func()//通過go關鍵字啟動一個協程來運行函數

go routine的調度原理和操作系統的線層調度是比較相似的。這里我們將介紹go routine的相關知識。

goroutine(有人也稱之為協程)本質上go的用戶級線程的實現,這種用戶級線程是運行在內核級線程之上。當我們在go程序中創(chuàng)建goroutine的時候,我們的這些routine將會被分配到不同的內核級線程中運行。一個內核級線程可能會負責多個routine的運行。而保證這些routine在內內核級線程安全、公平、高效運行的工作,就由調度器來實現。

goroutine內部原理

概念介紹

在進行實現原理之前,了解下一些關鍵性術語的概念。

并發(fā)

一個cpu上能同時執(zhí)行多項任務,在很短時間內,cpu來回切換任務執(zhí)行(在某段很短時間內執(zhí)行程序a,然后又迅速得切換到程序b去執(zhí)行),有時間上的重疊(宏觀上是同時的,微觀仍是順序執(zhí)行),這樣看起來多個任務像是同時執(zhí)行,這就是并發(fā)。

并行

當系統有多個CPU時,每個CPU同一時刻都運行任務,互不搶占自己所在的CPU資源,同時進行,稱為并行。

進程

cpu在切換程序的時候,如果不保存上一個程序的狀態(tài)(也就是我們常說的context--上下文),直接切換下一個程序,就會丟失上一個程序的一系列狀態(tài),于是引入了進程這個概念,用以劃分好程序運行時所需要的資源。因此進程就是一個程序運行時候的所需要的基本資源單位(也可以說是程序運行的一個實體)。

線程

cpu切換多個進程的時候,會花費不少的時間,因為切換進程需要切換到內核態(tài),而每次調度需要內核態(tài)都需要讀取用戶態(tài)的數據,進程一旦多起來,cpu調度會消耗一大堆資源,因此引入了線程的概念,線程本身幾乎不占有資源,他們共享進程里的資源,內核調度起來不會那么像進程切換那么耗費資源。

協程

協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此,協程能保留上一次調用時的狀態(tài)(即所有局部狀態(tài)的一個特定組合),每次過程重入時,就相當于進入上一次調用的狀態(tài),換種說法:進入上一次離開時所處邏輯流的位置。線程和進程的操作是由程序觸發(fā)系統接口,最后的執(zhí)行者是系統;協程的操作執(zhí)行者則是用戶自身程序,goroutine也是協程。

Go調度的組成

Go的調度主要有四個結構組成,分別是:

  • G:goroutine的核心結構,包括routine的棧、程序計數器pc、以及一些狀態(tài)信息等;
  • M:內核級線程。goroutine在M上運行。M中信息包括:正在運行的goroutine、等待運行的routine列表等。當然也包括操作系統線程相關信息,這些此處不討論。
  • P:processor,處理器,只要用于執(zhí)行goroutine,維護了一個goroutine列表。其實P是可以從屬于M的。當P從屬于(分配給)M的時候,表示P中的某個goroutine得以運行。當P不從屬于M的時候,表示P中的所有goroutine都需要等待被安排到內核級線程運行。
  • Sched:調度器,存儲、維護M,以及一個全局的goroutine等待隊列,以及其他狀態(tài)信息。

Go程序的啟動過程

  • 初始化Sched:一個存儲P的列表pidle。P的數量可以通過GOMAXPROCS設置;
  • 創(chuàng)建第一個goroutine。這個goroutine會創(chuàng)建一個M,這個內核級線程(sysmon)的工作是對goroutine進行監(jiān)控。之后,這個goroutine開始我們在main函數里面的代碼,此時,該goroutine就是我們說的主routine。

創(chuàng)建goroutine:

  • goroutine創(chuàng)建時指定了代碼段
  • 然后,goroutine被加入到P中去等待運行。
  • 這個新建的goroutine的信息包含:棧地址、程序計數器

創(chuàng)建內核級線程M

內核級線程由go的運行時根據實際情況創(chuàng)建,我們無法再go中創(chuàng)建內核級線程。那什么時候回創(chuàng)建內核級線程呢?當前程序等待運行的goroutine數量達到一定數量及存在空閑(為被分配給M)的P的時候,Go運行時就會創(chuàng)建一些M,然后將空閑的P分配給新建的內核級線程M,接著才是獲取、運行goroutine。創(chuàng)建M的接口函數如下:

// 創(chuàng)建M的接口函數
void newm(void (*fn)(void), P *p)

// 分配P給M
if(m != &runtime·m0) {Â
  acquirep(m->nextp);
  m->nextp = nil;
}
// 獲取goroutine并開始運行
schedule();

M的運行

static void schedule(void)
{
  G *gp;

  gp = runqget(m->p);
  if(gp == nil)
    gp = findrunnable();

 // 如果P的類別不止一個goroutine,且調度器中有空閑的的P,就喚醒其他內核級線程M
  if (m->p->runqhead != m->p->runqtail &&
    runtime·atomicload(&runtime·sched.nmspinning) == 0 &&
    runtime·atomicload(&runtime·sched.npidle) > 0) // TODO: fast atomic
    wakep();
 // 執(zhí)行goroutine
  execute(gp);
}

  • runqget: 從P中獲取goroutine即gp。gp可能為nil(如M剛創(chuàng)建時P為空;或者P的goroutine已經運行完了)。
  • findrunnable:尋找空閑的goroutine(從全局的goroutine等待隊列獲取goroutine;如果所有goroutine都已經被分配了,那么從其他M的P的goroutine的goroutine列表獲取一些)。如果獲取到goroutine,就將他放入P中,并執(zhí)行它;否則沒能獲取到任何的goroutine,該內核級線程進行系統調用sleep了。
  • wakep:當當前內核級線程M的P中不止一個goroutine且調度器中有空閑的的P,就喚醒其他內核級線程M。(為了找些空閑的M幫自己分擔)。

Routine狀態(tài)遷移

前面說的是G,M是怎樣創(chuàng)建的以及什么時候創(chuàng)建、運行。那么goroutine在M是是怎樣進行調度的呢?這個才是goroutine的調度核心問題,即上面代碼中的schedule。在說調度之前,我們必須知道goroutine的狀態(tài)有什么,以及各個狀態(tài)之間的關系。

  • Gidle:創(chuàng)建中的goroutine,實際上這個狀態(tài)沒有什么用;
  • Grunnable:新創(chuàng)建完成的goroutine在完成了資源的分配及初始化后,會進入這個狀態(tài)。這個新創(chuàng)建的goroutine會被分配到創(chuàng)建它的M的P中;
  • Grunning:當Grunnable中的goroutine等到了空閑的cpu或者到了自己的時間片的時候,就會進入Grunning狀態(tài)。這個裝下的goroutine可以被前文提到的findrunnable函數獲取;
  • Gwaiting:當正在運行的goroutine進行一些阻塞調用的時候,就會從Grunning狀態(tài)進入Gwaiting狀態(tài)。常見的調用有:寫入一個滿的channel、讀取空的channel、IO操作、定時器Ticker等。當阻塞調用完成后,goroutine的狀態(tài)就會從Gwaiting轉變?yōu)镚runnable;
  • Gsyscall:當正在運行的goroutine進行系統調用的時候,其狀態(tài)就會轉變?yōu)镚syscall。當系統調用完成后goroutine的狀態(tài)就會變?yōu)镚runnable。(前文提到的sysmon進程會監(jiān)控所有的P,如果發(fā)現有的P的系統調用是阻塞式的或者執(zhí)行的時間過長,就會將P從原來的M分離出來,并新建一個M,將P分配給這個新建的M)。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • go語言環(huán)境搭建簡述

    go語言環(huán)境搭建簡述

    本文簡單記錄了下go語言環(huán)境的搭建流程,給小伙伴們一個參考,希望大家能夠喜歡。
    2015-01-01
  • Go?interface?接口的最佳實踐經驗分享

    Go?interface?接口的最佳實踐經驗分享

    go?的接口在go的編程里面用的十分頻繁,尤其是空接口的使用,因為有了接口,才使得Go語言變得異常的強大,今天給大家介紹下Go?interface?接口的最佳實踐經驗分享,感興趣的朋友一起看看吧
    2022-04-04
  • Golang實現不被復制的結構體的方法

    Golang實現不被復制的結構體的方法

    sync包中的許多結構都是不允許拷貝的,因為它們自身存儲了一些狀態(tài)(比如等待者的數量),如果你嘗試復制這些結構體,就會在你的?IDE中看到警告,那這是怎么實現的呢,下文就來和大家詳細講講
    2023-03-03
  • Go結合MQTT實現通信的示例代碼

    Go結合MQTT實現通信的示例代碼

    本文主要介紹了Go結合MQTT實現通信的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • 使用go連接clickhouse的實戰(zhàn)操作

    使用go連接clickhouse的實戰(zhàn)操作

    這篇文章主要給大家介紹了關于使用go連接clickhouse的實戰(zhàn)操作,文中通過實例代碼介紹的非常詳細,對大家學習或者使用go具有一定的參考學習價值,需要的朋友可以參考下
    2023-03-03
  • Go語言學習之接口類型(interface)詳解

    Go語言學習之接口類型(interface)詳解

    接口是用來定義行為的類型,定義的行為不由接口直接實現,而由通過方法由定義的類型實現,本文就來和大家詳細講講Go語言中接口的使用吧
    2023-03-03
  • GO的range如何使用詳解

    GO的range如何使用詳解

    本文主要介紹了GO的range如何使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • Go每日一庫之zap日志庫的安裝使用指南

    Go每日一庫之zap日志庫的安裝使用指南

    這篇文章主要為大家介紹了Go每日一庫之zap安裝使用示例學習,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • 淺析Go語言中閉包的使用

    淺析Go語言中閉包的使用

    閉包是一個函數和其相關的引用環(huán)境組合的一個整體。本文主要為大家介紹一下Go語言中閉包的使用,文中的示例代碼講解詳細,對我們學習Go語言有一定幫助,需要的可以參考一下
    2022-12-12
  • 深入了解Golang為什么需要超時控制

    深入了解Golang為什么需要超時控制

    本文將介紹為什么需要超時控制,然后詳細介紹Go語言中實現超時控制的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-05-05

最新評論