「Golang成長之路」併發之Goroutine

ice_moss發表於2021-08-23

一、併發

併發程式設計表現為程式有若干個自主的活動單元組成,在今天的網際網路中,一個web伺服器可能一次處理上千個請求,而平板電腦和手機在渲染使用者介面的同時,後端還同步進行著計算和處理網路請求等。
在go語言中每一個併發執行的活動稱之為goroutine,而在我們最常見的main函式中其實也是一個goroutine(主goroutine),在此之前,我們所見的介紹語法或者程式都是順序執行的,但是在併發程式設計的領域裡,如果從順序程式設計獲取的直覺可能讓我們加倍迷茫。
在此我們需要理解什麼是併發:
百科:當有多個執行緒在操作時,如果系統只有一個CPU,則它根本不可能真正同時進行一個以上的執行緒,它只能把CPU執行時間劃分成若干個時間段,再將時間 段分配給各個執行緒執行,在一個時間段的執行緒程式碼執行時,其它執行緒處於掛起狀。.這種方式我們稱之為併發(Concurrent)。

「Golang成長之路」併發之Goroutine
(這個是普通函式的執行模式,main將其控制權交給呼叫的函式,最後在返回給main)

我的理解:舉個例子,我一邊在上網課,一邊在做筆記,我在做筆記的時候需要將網課暫停,等我的筆記做完後又繼續看網課,這兩件事就可以看作是一個併發的過程。

「Golang成長之路」併發之Goroutine

(協程則是:main和建立的goroutine是相互作用的,相互給予控制權,就像兩個人,各做各的事,並且他們也相互通訊)

二、go關鍵字

當一個程式啟動時,只有一個goroutine來呼叫main,稱他為主goroutine,新的goroutine透過go關鍵字來進行建立,看下面例子:

func main() {
   for i := 0; i < 3; i++ {
   //使用go關鍵字建立goroutine
   //匿名函式
       go func() {
          for j := 3; j >= 0; j-- {
            fmt.Println("gorouting", j)
          }
       }()
    fmt.Println("gorouting mian:", i)
    //控制程式執行時間
    time.Sleep(time.Microsecond)

   }
}

當然,也可以將匿名函式拿出來:

package main

import (
   "fmt"
 "time")

func doWorker() {
     for j := 3; j >= 0; j-- {
        fmt.Println("gorouting", j)
     }
}

func main() {
   for i := 0; i < 3; i++ {
   //使用go關鍵字建立goroutine
       go doWroker()
    fmt.Println("gorouting mian:", i)
    //控制程式執行時間
    time.Sleep(time.Microsecond)
   }
}

先來看看列印結果:

gorouting mian: 0
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting mian: 1
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting mian: 2
gorouting 3
gorouting 2
gorouting 1
gorouting 0

第二遍執行:

gorouting mian: 0
gorouting mian: 1
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting 3
gorouting 2
gorouting 1
gorouting 0
gorouting mian: 2
gorouting 3
gorouting 2
gorouting 1
gorouting 0

程式解釋:其實可以看出,每一遍的執行結果都是不一樣的,當程式加入第一個for , i=0時,執行到關鍵字go時,新的goroutine就建立了,但是程式不會立即執行新的goroutine,它會進行執行main中其餘的程式碼,在我們的這個例子中,第一遍執行,時就是這個結果,第二遍執行時也是同樣的。

func main(){
     var a[10] int
     for i := 0; i < 10;i++{
         //使用go關鍵字建立goroutine
         //匿名函式
          go func(i int){
             for{
                a[i]++
             }
          }(i)
       }
    time.Sleep(time.Microsecond)
    fmt.Println(a)
}

列印結果:

[39904 0 0 9122 0 7123 0 0 0 0]

程式解釋:在for迴圈中一共建立了10個goroutine,有的goroutine執行了,有的還處於等待狀態,但是我們給主goroutine的時間不多,所以有一些還來不及執行就被主goroutine殺掉了,這樣我們就能理解列印結果了。

三、goroutine切換點(可能)

goroutine可能切換:

  1. I/O、select
  2. channel
  3. 等待鎖
  4. 函式呼叫(有時)
  5. runtime.Gosched
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章