Go語言並發編程(一)

來源:互聯網
上載者:User

標籤:替代   max   轉移   .com   核心數   bsp   關於   指正   依次   

 Go語言的特色不得不提的就是並發機制,在C語言中編寫非常繁瑣複雜的並發程式在Go語言中可以非常便捷。 
  這幾天寫並發測試指令碼的時候,結合代碼和其他大牛的文章學習了一下。把自己的理解寫下來。如有錯誤,請指正。

一、並發與並行

  Go中並發程式主要通過goroutine和channel來實現。 
  這裡我想先解釋一下的是“並發”一詞,一開始把並發當做了並行,一直覺得代碼有問題,其實這兩者並不是一回事。 
  並發就是:兩個隊列,同時依次去訪問一個資源。而並行:兩個隊列,分別依次訪問兩個資源。 
  簡單來說,並發,就像一個人(cpu)喂2個孩子(程式),輪換著每人喂一口,表面上兩個孩子都在吃飯。並行,就是2個人喂2個孩子,兩個孩子也同時在吃飯。

程式碼範例

  以前我們調用多個線程分別列印輸出有序的數字時,系統的線程會搶佔式地輸出, 表現出來的是亂序地輸出。而多個goroutine並發執行結果是輸出一串有序的數字接著一串有序的數字。範例程式碼:

var quit chan int = make(chan int)func loop() {    for i := 0; i < 10; i++ {        fmt.Printf("%d ", i)    }    quit <- 0}func main() {    // 開兩個goroutine跑函數loop, loop函數負責列印10個數    go loop()    go loop()    //保證goroutine都執行完,主線程才結束    for i := 0; i < 2; i++ {        <- quit    }}

輸出結果:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

但我們以前用線程實現的結果是亂序的,比如這樣的:

0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
二、goroutine是如何執行的

  實際上,預設Go所有的goroutines只能在一個線程裡跑,而一個goroutine並不相當於一個線程,goroutine的出現正是為了替代原來的線程概念成為最小的調度單位。(一旦運行goroutine時,先去當前線程尋找,如果線程阻塞了,則被分配到閒置線程,若無空閑線程,就建立一個線程。注意的是,當goroutine執行完畢後,線程不會被回收,而是成為了閒置線程。) 
  如果當前goroutine不發生阻塞,它是不會讓出CPU給其他goroutine的, 在上面的代碼執行個體中,輸出會是一個一個goroutine進行,而如果使用sleep函數,則阻塞掉了當前goroutine, 當前goroutine主動讓其他goroutine執行, 所以形成了並發。

代碼執行個體

  使用goroutine想要達到真正的並行的效果也是可以的,解決方案有兩個: 
1、允許Go使用多核(runtime.GOMAXPROCS)

var quit chan int = make(chan int)func loop() {    for i := 0; i < 100; i++ { //為了觀察,跑多些        fmt.Printf("%d ", i)    }    quit <- 0}func main() {    runtime.GOMAXPROCS(2) // 最多使用2個核    go loop()    go loop()    for i := 0; i < 2; i++ {        <- quit    }}

2、手動顯式調動(runtime.Gosched)

func loop() {    for i := 0; i < 10; i++ {        runtime.Gosched() // 顯式地讓出CPU時間給其他goroutine        fmt.Printf("%d ", i)    }    quit <- 0}func main() {    go loop()    go loop()    for i := 0; i < 2; i++ {        <- quit    }}

執行結果:

0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

第二種主動讓出CPU時間的方式仍然是在單核裡跑。但手工地切換goroutine導致了看上去的“並行”。

三、runtime

runtime調度器是個很神奇的東西,但是我但願它不存在,我希望顯式調度能更為自然些,多核處理預設開啟。

關於runtime包幾個函數: 
Gosched() //讓出cpu 
NumCPU()//返回當前系統的CPU核心數量 
GOMAXPROCS() //設定最大的可同時使用的CPU核心數 
Goexit() //退出當前goroutine(但是defer語句會照常執行)

四、總結

  預設所有goroutine會在一個原生線程裡跑,也就是預設只使用一個CPU核。如果當前goroutine不發生阻塞,它是不會讓出CPU時間給其他同線程的goroutines的,這是Go運行時對goroutine的調度,我們也可以使用runtime包來手工調度。 
  若我們開啟多核,當一個goroutine發生阻塞,Go會自動地把與該goroutine處於同一系統線程的其他goroutines轉移到另一個系統線程上去,以使這些goroutines不阻塞。從而實現並行效果。

 

 

參考自: https://studygolang.com/articles/5463

Go語言並發編程(一)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.