<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>

          關(guān)于Go語(yǔ)言,你不得不知的并發(fā)模式!

          共 5296字,需瀏覽 11分鐘

           ·

          2021-04-12 02:12

          什么是并發(fā)?有哪些我們需要知道的并發(fā)模式?Go語(yǔ)言中的協(xié)程并發(fā)模型是怎樣的?什么是主 goroutine?它與我們自己?jiǎn)⒂玫钠渌鹓oroutine 有什么不同?

          本文就來(lái)為你一一解答!

          以下內(nèi)容節(jié)選自Go語(yǔ)言極簡(jiǎn)一本通:零基礎(chǔ)入門到項(xiàng)目實(shí)戰(zhàn)一書(shū)!



          --正文--

          并發(fā)

          串行程序,即程序的執(zhí)行順序和程序的編寫(xiě)順序一致,整個(gè)程序只有一個(gè)上下文,就是一個(gè)棧,一個(gè)堆。

          并發(fā)程序,則需要運(yùn)行多個(gè)上下文,對(duì)應(yīng)多個(gè)調(diào)用棧。每個(gè)進(jìn)程在運(yùn)行時(shí),都有自己的調(diào)用棧和堆,有一套完整的上下文。操作系統(tǒng)在調(diào)用時(shí),會(huì)保證被調(diào)度進(jìn)程的上下文環(huán)境,待該進(jìn)程獲得時(shí)間后,再將該進(jìn)程的上下文恢復(fù)到系統(tǒng)中。

          串行的代碼是逐行執(zhí)行的,是確定的,而并發(fā)引入了不確定性。線程通信只能采用共享內(nèi)存的方式,為了保證共享內(nèi)存的有效性,可以加鎖,但是這樣又引入了死鎖的風(fēng)險(xiǎn)。

          并發(fā)的優(yōu)勢(shì)如下:

          (1)可以充分利用CPU 核心的優(yōu)勢(shì),提高程序的執(zhí)行效率。

          (2)并發(fā)能充分利用CPU 與其他硬件設(shè)備的異步性,如文件操作等。

          下面介紹3種并發(fā)模式。

          1.多進(jìn)程是操作系統(tǒng)層面的并發(fā)模式

          所有的進(jìn)程都由內(nèi)核管理。進(jìn)程描述的是程序的執(zhí)行過(guò)程,是運(yùn)行著的程序。

          一個(gè)進(jìn)程其實(shí)就是一個(gè)程序運(yùn)行時(shí)的產(chǎn)物。

          電腦為什么可以同時(shí)運(yùn)行那么多應(yīng)用程序?手機(jī)為什么可以有那么多App 同時(shí)在后臺(tái)刷新?

          這是因?yàn)樵谒鼈兊牟僮飨到y(tǒng)之上有多個(gè)代表著不同應(yīng)用程序的進(jìn)程在同時(shí)運(yùn)行。

          操作系統(tǒng)會(huì)為每個(gè)獨(dú)立的程序創(chuàng)建一個(gè)進(jìn)程,進(jìn)程可以裝下整個(gè)程序需要的資源。例如,程序執(zhí)行的進(jìn)度、執(zhí)行的結(jié)果等,都可以放在里面。在程序運(yùn)行結(jié)束后,再把進(jìn)程銷毀,然后運(yùn)行下一個(gè)程序,周而復(fù)始。

          進(jìn)程在程序運(yùn)行中是非常占用資源的,無(wú)論是否會(huì)用到全部的資源,只要程序啟動(dòng)了,就會(huì)被加載到進(jìn)程中。

          優(yōu)勢(shì)是進(jìn)程互不影響,劣勢(shì)是開(kāi)銷非常大。

          2.多線程屬于系統(tǒng)層面的并發(fā)模式,也是使用最多、最有效的一種模式

          線程是在進(jìn)程之內(nèi)的,可以把它理解為輕量級(jí)的進(jìn)程。它可以被視為進(jìn)程中代碼的執(zhí)行流程。這樣在處理程序的運(yùn)行和記錄中間結(jié)果時(shí),就可以使用更少的資源。待資源用完,線程就會(huì)被銷毀。

          線程要比進(jìn)程輕量級(jí)很多。一個(gè)進(jìn)程至少包含一個(gè)線程。如果一個(gè)進(jìn)程只包含一個(gè)線程,那么它里面的所有代碼都只會(huì)被串行地執(zhí)行。

          每個(gè)進(jìn)程的第一個(gè)線程都會(huì)隨著該進(jìn)程的啟動(dòng)而被創(chuàng)建,它們被稱為其所屬進(jìn)程的主線程。同理,如果一個(gè)進(jìn)程中包含多個(gè)線程,那么其中的代碼就可以被并發(fā)地執(zhí)行。

          除進(jìn)程的第一個(gè)線程外,其他的線程都是由進(jìn)程中已存在的線程創(chuàng)建出來(lái)的。也就是說(shuō),主線程之外的其他線程都只能由代碼顯式地創(chuàng)建和銷毀。這需要我們?cè)诰帉?xiě)程序時(shí)進(jìn)行手動(dòng)控制。

          優(yōu)勢(shì)是比進(jìn)程開(kāi)銷小一些,劣勢(shì)是開(kāi)銷仍然較大。

          3.goroutine

          從本質(zhì)上說(shuō),goroutine 是一種用戶態(tài)線程,不需要操作系統(tǒng)進(jìn)行搶占式調(diào)度。

          在Go 程序中,Go 語(yǔ)言的運(yùn)行時(shí)系統(tǒng)會(huì)自動(dòng)地創(chuàng)建和銷毀系統(tǒng)級(jí)的線程。

          系統(tǒng)級(jí)線程指的是操作系統(tǒng)提供的線程,而對(duì)應(yīng)的用戶級(jí)線程(goroutine)指的是架設(shè)在系統(tǒng)級(jí)線程之上的,由用戶(或者說(shuō)我們編寫(xiě)的程序)完全控制的代碼執(zhí)行流程。

          用戶級(jí)線程的創(chuàng)建、銷毀、調(diào)度、狀態(tài)變更,以及其中的代碼和數(shù)據(jù)都完全需要我們的程序自己去實(shí)現(xiàn)和處理,其優(yōu)勢(shì)如下:

          (1)因?yàn)樗鼈兊膭?chuàng)建和銷毀不需要通過(guò)操作系統(tǒng)去做,所以速度很快,可以提高任務(wù)并發(fā)性。編程簡(jiǎn)單、結(jié)構(gòu)清晰。

          (2)由于不用操作系統(tǒng)去調(diào)度它們的運(yùn)行,所以很容易控制,并且很靈活。

          協(xié)程并發(fā)模型

          在Go 語(yǔ)言中,不要通過(guò)共享數(shù)據(jù)來(lái)通信,恰恰相反,要通過(guò)通信的方式來(lái)共享數(shù)據(jù)。

          Go 語(yǔ)言不僅有g(shù)oroutine,還有強(qiáng)大的用來(lái)調(diào)度 goroutine、對(duì)接系統(tǒng)級(jí)線程的調(diào)度器。

          調(diào)度器是 Go 語(yǔ)言運(yùn)行時(shí)系統(tǒng)的重要組成部分,它主要負(fù)責(zé)統(tǒng)籌調(diào)配 Go 并發(fā)編程模型中的三個(gè)主要元素,即G(goroutine 的縮寫(xiě))、P(processor 的縮寫(xiě))和 M(machine 的縮寫(xiě)),如下圖所示。

           

          其中,M 指的就是系統(tǒng)級(jí)線程。而P 指的是一種可以引用若干個(gè)G,且能夠使這些G 在恰當(dāng)?shù)臅r(shí)機(jī)與M 進(jìn)行對(duì)接,并得到運(yùn)行的中介。

          從宏觀上說(shuō),由于P 的存在,G 和M 可以呈現(xiàn)出多對(duì)多的關(guān)系。當(dāng)一個(gè)正在與某個(gè)M 對(duì)接并運(yùn)行著的G,需要因某個(gè)事件(比如等待 I/O 或鎖的解除)而暫停運(yùn)行時(shí),調(diào)度器總會(huì)及時(shí)地發(fā)現(xiàn),并把這個(gè)G 與那個(gè)M 分離開(kāi),以釋放計(jì)算資源供那些等待運(yùn)行的G 使用。

          而當(dāng)一個(gè)G 需要恢復(fù)運(yùn)行時(shí),調(diào)度器又會(huì)盡快地為它尋找空閑的計(jì)算資源(包括M)并安排運(yùn)行。另外,當(dāng)M 不夠用時(shí),調(diào)度器會(huì)向操作系統(tǒng)申請(qǐng)新的系統(tǒng)級(jí)線程,而當(dāng)某個(gè)M 已無(wú)用時(shí),調(diào)度器又會(huì)負(fù)責(zé)把它及時(shí)地銷毀。

          程序中的所有 goroutine 也都會(huì)被充分地調(diào)度,其中的代碼也都會(huì)被并發(fā)地運(yùn)行,即使goroutine 數(shù)以十萬(wàn)計(jì),仍然可以如此。

          什么是主 goroutine?它與我們自己?jiǎn)⒂玫钠渌鹓oroutine 有什么不同?

          先來(lái)看下面的代碼:

          package main
          import "fmt"
          func main() { for i := 0; i < 10; i++ { go func() { fmt.Println(i) }() }}

          這段代碼只在main 函數(shù)中寫(xiě)了一條for 語(yǔ)句。這條for 語(yǔ)句中的代碼會(huì)迭代運(yùn)行10 次,并有一個(gè)局部變量i 表示當(dāng)次迭代的序號(hào),該序號(hào)是從0 開(kāi)始的。在這條for 語(yǔ)句中僅有一條Go語(yǔ)句,在這條Go 語(yǔ)句中也僅有一條語(yǔ)句,該語(yǔ)句調(diào)用了fmt.Println 函數(shù),想要打印出變量i 的值。

          這個(gè)程序很簡(jiǎn)單,只有三條語(yǔ)句。這個(gè)程序被執(zhí)行后,會(huì)打印出什么內(nèi)容呢?

          答案是:大部分計(jì)算機(jī)執(zhí)行后,屏幕上不會(huì)有任何內(nèi)容被打印出來(lái)。

          這是為什么呢?

          一個(gè)進(jìn)程總會(huì)有一個(gè)主線程,類似地,每一個(gè)獨(dú)立的Go 程序在運(yùn)行時(shí)也總會(huì)有一個(gè)主goroutine。這個(gè)主goroutine 會(huì)在Go 程序的運(yùn)行準(zhǔn)備工作完成后被自動(dòng)地啟用。

          一般來(lái)說(shuō),每條Go 語(yǔ)句都帶有一個(gè)函數(shù)調(diào)用,這個(gè)被調(diào)用的函數(shù)就是Go 函數(shù)。而主goroutine 的Go 函數(shù)就是那個(gè)作為程序入口的main 函數(shù)。Go 函數(shù)執(zhí)行的時(shí)間與其所屬的Go語(yǔ)句執(zhí)行的時(shí)間不同。

          如下圖所示,當(dāng)程序執(zhí)行到一條Go 語(yǔ)句時(shí),Go 語(yǔ)言的運(yùn)行時(shí)系統(tǒng)會(huì)先試圖從某個(gè)空閑的G 隊(duì)列中獲取一個(gè)G(也就是goroutine),只有在找不到空閑G 的情況下它才會(huì)去創(chuàng)建一個(gè)新的G。

          如果已經(jīng)存在一個(gè)goroutine,那么已存在的goroutine 總是會(huì)被優(yōu)先復(fù)用。如果不存在,就去啟動(dòng)另一個(gè)goroutine。

          在Go 語(yǔ)言中,創(chuàng)建G 的成本非常低。創(chuàng)建一個(gè)G 并不需要像新建一個(gè)進(jìn)程或者一個(gè)系統(tǒng)級(jí)線程那樣,必須通過(guò)操作系統(tǒng)的系統(tǒng)調(diào)用來(lái)完成,而是在 Go 語(yǔ)言的運(yùn)行時(shí)系統(tǒng)內(nèi)部就可以完全做到,一個(gè)G 僅相當(dāng)于為需要并發(fā)執(zhí)行代碼片段服務(wù)的上下文環(huán)境。

          在拿到一個(gè)空閑的G 之后,Go 語(yǔ)言運(yùn)行時(shí)系統(tǒng)會(huì)用這個(gè)G 去包裝當(dāng)前的那個(gè)Go 函數(shù)(或者一個(gè)匿名的函數(shù)),然后再把這個(gè)G 追加到某個(gè)可運(yùn)行的G 隊(duì)列中。隊(duì)列中的G 總是按照先入先出的順序,由運(yùn)行時(shí)系統(tǒng)安排運(yùn)行。

          由于上面所說(shuō)的那些準(zhǔn)備工作是不可避免的,所以會(huì)消耗一定時(shí)間。因此,Go 函數(shù)的執(zhí)行時(shí)間總是慢于它所屬的Go 語(yǔ)句的執(zhí)行時(shí)間。

          明白了這些之后,再來(lái)看上面的例子。請(qǐng)記住,只要Go 語(yǔ)句本身執(zhí)行完畢,Go 程序不會(huì)等待Go 函數(shù)的執(zhí)行,它就會(huì)立刻執(zhí)行后邊的語(yǔ)句,這就是異步并發(fā)執(zhí)行。

          這里“后邊的語(yǔ)句”一般指的是上面例子中 for 語(yǔ)句中的下一個(gè)迭代。當(dāng)最后一個(gè)迭代運(yùn)行時(shí),這個(gè)“后邊的語(yǔ)句”是不存在的。

          上面的那條for 語(yǔ)句會(huì)以很快的速度執(zhí)行完畢。當(dāng)它執(zhí)行完畢時(shí),那10 個(gè)包裝了Go 函數(shù)的 goroutine 往往還沒(méi)有獲得運(yùn)行的機(jī)會(huì)。Go 函數(shù)中的那個(gè)對(duì)fmt.Println 函數(shù)的調(diào)用是以for 語(yǔ)句中的變量i 作為參數(shù)的。

          當(dāng)for 語(yǔ)句執(zhí)行完畢時(shí),這些Go 函數(shù)都還沒(méi)有執(zhí)行,那么它們引用的變量i 是多少呢?

          一旦主 goroutine 中的代碼(也就是main 函數(shù)中的那些代碼)執(zhí)行完畢,當(dāng)前的Go 程序就會(huì)結(jié)束運(yùn)行。當(dāng)Go 程序結(jié)束運(yùn)行時(shí),無(wú)論其他的goroutine 是否運(yùn)行,都不會(huì)被執(zhí)行了。當(dāng)for語(yǔ)句的最后一個(gè)迭代運(yùn)行時(shí),其中的那條Go 語(yǔ)句即最后一條語(yǔ)句。所以,在執(zhí)行完這條Go語(yǔ)句之后,主goroutine 中的代碼就執(zhí)行完了,Go 程序會(huì)立即結(jié)束運(yùn)行。因此前面的代碼不會(huì)有任何內(nèi)容被打印輸出。

          嚴(yán)謹(jǐn)?shù)刂v,Go 語(yǔ)言并不管這些goroutine 以怎樣的順序運(yùn)行。由于主goroutine 會(huì)與我們自己?jiǎn)⒂玫钠渌?goroutine 一起被調(diào)度,而調(diào)度器很可能會(huì)在goroutine 中的代碼只執(zhí)行了一部分的時(shí)候暫停,以便所有的goroutine 都有運(yùn)行的機(jī)會(huì)。所以哪個(gè)goroutine 先執(zhí)行完,哪個(gè)goroutine后執(zhí)行完往往是不可預(yù)知的。

          對(duì)于上面簡(jiǎn)單的代碼而言,絕大多數(shù)情況都是“不會(huì)有任何內(nèi)容被打印出來(lái)”。但是為了嚴(yán)謹(jǐn)起見(jiàn),無(wú)論回答“打印出 10 個(gè)10”,還是“不會(huì)有任何內(nèi)容被打印出來(lái)”,或是“打印出亂序的0 到9”都是對(duì)的。

          這個(gè)原理非常重要,希望讀者能理解。



          ▊《Go語(yǔ)言極簡(jiǎn)一本通:零基礎(chǔ)入門到項(xiàng)目實(shí)戰(zhàn)

          歡喜 編著


          • 一本就通,一學(xué)就會(huì)!

          本書(shū)是一本Go語(yǔ)言入門書(shū),全書(shū)共分為三部分。第一部分講解Go語(yǔ)言基礎(chǔ)知識(shí),包括變量與簡(jiǎn)單類型、數(shù)組、切片、流程控制、字典、函數(shù)、結(jié)構(gòu)體與方法、接口等,可以幫助讀者快速掌握Go語(yǔ)言的基本程序結(jié)構(gòu)。第二部分講解Go語(yǔ)言高效并發(fā)相關(guān)知識(shí),包括協(xié)程與通道、并發(fā)資源、包管理和測(cè)試等,讓讀者對(duì)Go語(yǔ)言層面的并發(fā)支持有更深入的理解。第三部分講解Go語(yǔ)言項(xiàng)目實(shí)戰(zhàn),包括Gin框架、生活點(diǎn)評(píng)項(xiàng)目實(shí)戰(zhàn)、賬戶管理系統(tǒng)實(shí)戰(zhàn),以及OAuth 2.0的授權(quán)協(xié)議等。通過(guò)實(shí)戰(zhàn),把前面講解的知識(shí)點(diǎn)運(yùn)用起來(lái),幫助讀者快速上手,積累項(xiàng)目經(jīng)驗(yàn)。

          (掃碼了解本書(shū)詳情)



              


          如果喜歡本文
          歡迎 在看留言分享至朋友圈 三連


           熱文推薦  





          ▼點(diǎn)擊閱讀原文,獲取本書(shū)詳情~
          瀏覽 19
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  色婷婷综合激情 | 久久久精品网站 | 蜜乳一二三四区 | 中文字幕在线观看 | 欧美成人网站网址 |