協程 Goroutine 的基本使用
阿新 • • 發佈:2022-04-08
注意,main函式也是個goroutine。
基本使用
使用go執行子任務,會交替執行(和時間片一樣)。
主goroutine退出後,其它的工作goroutine也會自動退出(有點父子程序的感覺):
package main import ( "fmt" "time" ) func newTask() { i := 0 for { i++ fmt.Printf("new goroutine: i = %d\n", i) time.Sleep(1 * time.Second) //延時1s } } func main() { //建立一個 goroutine,啟動另外一個任務 go newTask() i := 0 //main goroutine 迴圈列印 for { i++ fmt.Printf("main goroutine: i = %d\n", i) time.Sleep(1 * time.Second) //延時1s } //這裡是加入了死迴圈,如果去掉,則程式會直接退出。 }
多個協程的順序是不一定的。
var _ = runtime.GOMAXPROCS(3)
var a, b int
func u1() {
a = 1
b = 2
}
func u2() {
a = 3
b = 4
}
func p() {
println(a)
println(b)
}
func main() {
go u1() // 多個 goroutine 的執行順序不定
go u2()
go p()
time.Sleep(1 * time.Second)
}
runtime包
讓出時間片
runtime.Gosched() //讓別人先執行,需要同時要時間片的時候才會有效,對方如果已經停了就還是自己執行。
runtime.Goexit() //將立即終止當前 goroutine 執⾏,排程器確保所有已註冊 defer延遲呼叫被執行。
package main import ( "fmt" "runtime" "time" ) //呼叫 runtime.Goexit() 將立即終止當前 goroutine 執⾏,排程器確保所有已註冊 defer延遲呼叫被執行。 func main() { go func() { defer fmt.Println("A.defer") func() { defer fmt.Println("B.defer") runtime.Goexit() // 終止當前 goroutine, import "runtime" fmt.Println("B") // 不會執行 }() defer fmt.Println("C.defer") //還沒來得及註冊,不會執行 fmt.Println("A") // 不會執行 }() //別忘了() //死迴圈,目的不讓主goroutine結束 for { time.Sleep(1 * time.Second) } } //執行結果: //B.defer //A.defer
GOMAXPROCS
呼叫 runtime.GOMAXPROCS() 用來設定可以平行計算的CPU核數的最大值,並返回之前的值。
示例程式碼:
func main() {
//n := runtime.GOMAXPROCS(1)
//列印結果:111111111111111111110000000000000000000011111...
n := runtime.GOMAXPROCS(2)
//列印結果:010101010101010101011001100101011010010100110...
fmt.Printf("n = %d\n", n)
for {
go fmt.Print(0)
fmt.Print(1)
}
}
在第一次執行(runtime.GOMAXPROCS(1))時,最多同時只能有一個goroutine被執行。所以會列印很多1。
過了一段時間後,GO排程器會將其置為休眠,並喚醒另一個goroutine,這時候就開始列印很多0了,在列印的時候,goroutine是被排程到作業系統執行緒上的。
在第二次執行(runtime.GOMAXPROCS(2))時,我們使用了兩個CPU,所以兩個goroutine可以一起被執行,以同樣的頻率交替列印0和1。