溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Go語言之goroutine

發(fā)布時(shí)間:2020-07-11 07:09:41 來源:網(wǎng)絡(luò) 閱讀:276 作者:baby神 欄目:編程語言

在談goroutine之前,我們先談?wù)劜l(fā)和并行。


一般的程序,如果沒有特別要求的話,是順序執(zhí)行的,這樣的程序也容易編寫維護(hù)。但是隨著科技的發(fā)展、業(yè)務(wù)的演進(jìn),我們不得不變寫可以并行的程序,因?yàn)檫@樣有很多好處。


比如你在看文章的時(shí)候,還可以聽著音樂,這就是系統(tǒng)的并行,同時(shí)可以做多件事情,充分地利用計(jì)算機(jī)的多核,提升軟件運(yùn)行的性能。


在操作系統(tǒng)中,有兩個(gè)重要的概念:一個(gè)是進(jìn)程、一個(gè)是線程。當(dāng)我們運(yùn)行一個(gè)程序的時(shí)候,比如你的IDE或者QQ等,操作系統(tǒng)會(huì)為這個(gè)程序創(chuàng)建一個(gè)進(jìn)程,這個(gè)進(jìn)程包含了運(yùn)行這個(gè)程序所需的各種資源,可以說它是一個(gè)容器,是屬于這個(gè)程序的工作空間,比如它里面有內(nèi)存空間、文件句柄、設(shè)備和線程等。


那么線程是什么呢?線程是一個(gè)執(zhí)行的空間,比如要下載一個(gè)文件,訪問一次網(wǎng)絡(luò)等。線程會(huì)被操作系統(tǒng)調(diào)用,來在不同的處理器上運(yùn)行編寫的代碼任務(wù),這個(gè)處理器不一定是該程序進(jìn)程所在的處理。操作系統(tǒng)的調(diào)度是操作系統(tǒng)負(fù)責(zé)的,不同的操作系統(tǒng)可能會(huì)不一樣,但是對(duì)于我們程序編寫者來說,不用關(guān)心,因?yàn)閷?duì)我們都是透明的。


一個(gè)進(jìn)程在啟動(dòng)的時(shí)候,會(huì)創(chuàng)建一個(gè)主線程,這個(gè)主線程結(jié)束的時(shí)候,程序進(jìn)程也就終止了,所以一個(gè)進(jìn)程至少有一個(gè)線程。這也是我們?cè)趍ain函數(shù)里,使用goroutine的時(shí)候,要讓主線程等待的原因,因?yàn)橹骶€程結(jié)束了,程序就終止了,那么就有可能會(huì)看不到goroutine的輸出。


Go語言中并發(fā)指的是讓某個(gè)函數(shù)獨(dú)立于其他函數(shù)運(yùn)行的能力,一個(gè)goroutine就是一個(gè)獨(dú)立的工作單元,Go的runtime(運(yùn)行時(shí))會(huì)在邏輯處理器上調(diào)度這些goroutine來運(yùn)行,一個(gè)邏輯處理器綁定一個(gè)操作系統(tǒng)線程,所以說goroutine不是線程,它是一個(gè)協(xié)程,也是這個(gè)原因,它是由Go語言運(yùn)行時(shí)本身的算法實(shí)現(xiàn)的。


這里我們總結(jié)下幾個(gè)概念:


概念

說明

進(jìn)程

一個(gè)程序?qū)?yīng)一個(gè)獨(dú)立程序空間

線程

一個(gè)執(zhí)行空間,一個(gè)進(jìn)程可以有多個(gè)線程

邏輯處理器

執(zhí)行創(chuàng)建的goroutine,綁定一個(gè)線程

調(diào)度器

Go運(yùn)行時(shí)中的,分配goroutine給不同的邏輯處理器

全局運(yùn)行隊(duì)列

所有剛創(chuàng)建的goroutine都會(huì)放到這里

本地運(yùn)行隊(duì)列

邏輯處理器的goroutine隊(duì)列


當(dāng)我們創(chuàng)建一個(gè)goroutine后,會(huì)先存放在全局運(yùn)行隊(duì)列中,等待Go運(yùn)行時(shí)的調(diào)度器進(jìn)行調(diào)度。把他們分配給其中的一個(gè)邏輯處理器,并放到這個(gè)邏輯處理器對(duì)應(yīng)的本地運(yùn)行隊(duì)列中,最終等著被邏輯處理器執(zhí)行即可。


這一套管理、調(diào)度、執(zhí)行g(shù)oroutine的方式稱之為Go的并發(fā)。并發(fā)可以同時(shí)做很多事情,比如有個(gè)goroutine執(zhí)行了一半,就被暫停執(zhí)行其他goroutine去了,這是Go控制管理的。所以并發(fā)的概念和并行不一樣,并行指的是在不同的物理處理器上同時(shí)執(zhí)行不同的代碼片段,并行可以同時(shí)做很多事情;并發(fā)是同時(shí)管理很多事情,因?yàn)椴僮飨到y(tǒng)和硬件的總資源比較少,所以并發(fā)的效果要比并行好的多,使用較少的資源做更多的事情,也是Go語言提倡的。


Go的并發(fā)原理我們剛剛講了,那么Go的并行是怎樣的呢?其實(shí)答案非常簡單,多創(chuàng)建一個(gè)邏輯處理器就好了,這樣調(diào)度器就可以同時(shí)分配全局運(yùn)行隊(duì)列中的goroutine到不同的邏輯處理器上并行執(zhí)行。


func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    go func(){
        defer wg.Done()
        for i:=1;i<100;i++ {
            fmt.Println("A:",i)
        }
    }()
    go func(){
        defer wg.Done()
        for i:=1;i<100;i++ {
            fmt.Println("B:",i)
        }
    }()
    wg.Wait()
}


這是一個(gè)簡單的并發(fā)程序。創(chuàng)建一個(gè)goroutine是通過go關(guān)鍵字的,其后跟一個(gè)函數(shù)或者方法即可。


這里的sync.WaitGroup其實(shí)是一個(gè)計(jì)數(shù)的信號(hào)量,使用它的目的是要main函數(shù)等待兩個(gè)goroutine執(zhí)行完成后再結(jié)束,不然這兩個(gè)goroutine還在運(yùn)行的時(shí)候,程序就結(jié)束了,看不到想要的結(jié)果。


sync.WaitGroup的使用也非常簡單,先是使用Add方法設(shè)置計(jì)算器為 2 ,每一個(gè)goroutine的函數(shù)執(zhí)行完之后,就調(diào)用Done方法減 1 。Wait方法的意思是如果計(jì)數(shù)器大于 0 ,就會(huì)阻塞,所以main函數(shù)會(huì)一直等待兩個(gè)goroutine完成后,再結(jié)束。


我們運(yùn)行這個(gè)程序,發(fā)現(xiàn)A和B前綴會(huì)交叉出現(xiàn),并且每次運(yùn)行的結(jié)果可能不一樣,這就是Go調(diào)度器調(diào)度的結(jié)果。


默認(rèn)情況下,Go默認(rèn)是給每個(gè)可用的物理處理器都分配一個(gè)邏輯處理器,因?yàn)槲业碾娔X是 4 核的,所以上面的例子默認(rèn)創(chuàng)建了 4 個(gè)邏輯處理器,所以這個(gè)例子中同時(shí)也有并行的調(diào)度,如果我們強(qiáng)制只使用一個(gè)邏輯處理器,我們?cè)倏纯唇Y(jié)果。


func main() {
    runtime.GOMAXPROCS(1)
    var wg sync.WaitGroup
    wg.Add(2)
    go func(){
        defer wg.Done()
        for i:=1;i<100;i++ {
            fmt.Println("A:",i)
        }
    }()
    go func(){
        defer wg.Done()
        for i:=1;i<100;i++ {
            fmt.Println("B:",i)
        }
    }()
    wg.Wait()
}


設(shè)置邏輯處理器個(gè)數(shù)也非常簡單,在程序開頭使用runtime.GOMAXPROCS(1)即可,這里設(shè)置的數(shù)量是 1 。我們這時(shí)候再運(yùn)行,會(huì)發(fā)現(xiàn)先打印A,再打印B。


這里我們不要誤認(rèn)為是順序執(zhí)行,這里之所以順序輸出的原因,是因?yàn)槲覀兊膅oroutine執(zhí)行時(shí)間太短暫了,還沒來得及切換到第 2 個(gè)goroutine,第 1 個(gè)goroutine就完成了。這里我們可以把每個(gè)goroutine的執(zhí)行時(shí)間拉長一些,就可以看到并發(fā)的效果了,這里不再示例了,大家自己試試。


對(duì)于邏輯處理器的個(gè)數(shù),不是越多越好,要根據(jù)電腦的實(shí)際物理核數(shù)。如果不是多核的,設(shè)置再多的邏輯處理器個(gè)數(shù)也沒用。如果需要設(shè)置的話,一般我們采用如下代碼設(shè)置。


runtime.GOMAXPROCS(runtime.NumCPU())


所以對(duì)于并發(fā)來說,就是Go語言本身自己實(shí)現(xiàn)的調(diào)度;對(duì)于并行來說,是和運(yùn)行的電腦的物理處理器的核數(shù)有關(guān)的,多核就可以并行并發(fā),單核只能并發(fā)了。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI