Go語言實戰筆記(十二)| Go goroutine

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org,第一時間看後續筆記。覺得有協助的話,順手分享到朋友圈吧,感謝支援。

在談goroutine之前,我們先談談並發和並行。

一般的程式,如果沒有特別的要求的話,是順序執行的,這樣的程式也容易編寫維護。但是隨著科技的發展、業務的演化,我們不得不變寫可以並行的程式,因為這樣有很多好處。

比如你在看文章的時候,還可以聽著音樂,這就是系統的並行,同時可以做多件事情,充分的利用電腦的多核,提升的軟體啟動並執行效能。

在作業系統中,有兩個重要的概念:一個是進程、一個是線程。當我們運行一個程式的時候,比如你的IDE或者QQ等,作業系統會為這個程式建立一個進程,這個進程包含了運行這個程式所需的各種資源,可以說它是一個容器,是屬於這個程式的工作空間,比如它裡面有記憶體空間、檔案控制代碼、裝置和線程等等。

那麼線程是什麼呢?線程是一個執行的空間,比如要下載一個檔案,訪問一次網路等等。線程會被作業系統調用,來在不同的處理器上運行編寫的代碼任務,這個處理器不一定是該程式進程所在的處理。作業系統過的調度是作業系統負責的,不同的作業系統可能會不一樣,但是對於我們程式編寫者來說,不用關心,因為對我們都是透明的。

一個進程在啟動的時候,會建立一個主線程,這個主線程結束的時候,程式進程也就終止了,所以一個進程至少有一個線程,這也是我們在main函數裡,使用goroutine的時候,要讓主線程等待的原因,因為主線程結束了,程式就終止了,那麼就有可能會看不到goroutine的輸出。

go語言中並髮指的是讓某個函數獨立於其他函數啟動並執行能力,一個goroutine就是一個獨立的工作單元,Go的runtime(運行時)會在邏輯處理器上調度這些goroutine來運行,一個邏輯處理器綁定一個作業系統線程,所以說goroutine不是線程,它是一個協程,也是這個原因,它是由Go語言運行時本身的演算法實現的。

這裡我們總結下幾個概念:

概念 說明
進程 一個程式對應一個獨立程式空間
線程 一個執行空間,一個進程可以有多個線程
邏輯處理器 執行建立的goroutine,綁定一個線程
調度器 Go運行時中的,分配goroutine給不同的邏輯處理器
全域運行隊列 所有剛建立的goroutine都會放到這裡
本地運行隊列 邏輯處理器的goroutine隊列

當我們建立一個goroutine的後,會先存放在全域運行隊列中,等待Go運行時的調度器進行調度,把他們分配給其中的一個邏輯處理器,並放到這個邏輯處理器對應的本地運行隊列中,最終等著被邏輯處理器執行即可。

這一套管理、調度、執行goroutine的方式稱之為Go的並發。並發可以同時做很多事情,比如有個goroutine執行了一半,就被暫停執行其他goroutine去了,這是Go控制管理的。所以並發的概念和並行不一樣,並行指的是在不同的物理處理器上同時執行不同的程式碼片段,並行可以同時做很多事情,而並發是同時管理很多事情,因為作業系統和硬體的總資源比較少,所以並發的效果要比並行好的多,使用較少的資源做更多的事情,也是Go語言提倡的。

Go的並發原理我們剛剛講了,那麼Go的並行是怎樣的呢?其實答案非常簡單,多建立一個邏輯處理器就好了,這樣調度器就可以同時分配全域運行隊列中的goroutine到不同的邏輯處理器上並存執行。

1234567891011121314151617
func main() {var wg sync.WaitGroupwg.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()}

這是一個簡單的並發程式。建立一個goroutine是通過go 關鍵字的,其後跟一個函數或者方法即可。

這裡的sync.WaitGroup其實是一個計數的訊號量,使用它的目的是要main函數等待兩個goroutine執行完成後再結束,不然這兩個goroutine還在啟動並執行時候,程式就結束了,看不到想要的結果。

sync.WaitGroup的使用也非常簡單,先是使用Add 方法設設定計算機為2,每一個goroutine的函數執行完之後,就調用Done方法減1。Wait方法的意思是如果計數器大於0,就會阻塞,所以main 函數會一直等待2個goroutine完成後,再結束。

我們運行這個程式,會發現A和B首碼會交叉出現,並且每次啟動並執行結果可能不一樣,這就是Go調度器調度的結果。

預設情況下,Go預設是給每個可用的物理處理器都分配一個邏輯處理器,因為我的電腦是4核的,所以上面的例子預設建立了4個邏輯處理器,所以這個例子中同時也有並行的調度,如果我們強制只使用一個邏輯處理器,我們再看看結果。

123456789101112131415161718
func main() {runtime.GOMAXPROCS(1)var wg sync.WaitGroupwg.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()}

設定邏輯處理器個數也非常簡單,在程式開頭使用runtime.GOMAXPROCS(1)即可,這裡設定的數量是1。我們這時候再運行,會發現先列印A,再列印B。

這裡我們不要誤認為是順序執行,這裡之所以順序輸出的原因,是因為我們的goroutine執行時間太短暫了,還沒來得及切換到第2個goroutine,第1個goroutine就完成了。這裡我們可以把每個goroutine的執行時間拉長一些,就可以看到並發的效果了,這裡不再樣本了,大家自己試試。

對於邏輯處理器的個數,不是越多越好,要根據電腦的實際物理核心數,如果不是多核的,設定再多的邏輯處理器個數也沒用,如果需要設定的話,一般我們採用如下代碼設定。

1
runtime.GOMAXPROCS(runtime.NumCPU())

所以對於並發來說,就是Go語言本身自己實現的調度,對於並行來說,是和啟動並執行電腦的物理處理器的核心數有關的,多核就可以並行並發,單核只能並發了。

《Go語言實戰》讀書筆記,未完待續,歡迎掃碼關注公眾號flysnow_org,第一時間看後續筆記。覺得有協助的話,順手分享到朋友圈吧,感謝支援。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.