今天這篇筆記重點講goroutine
首先怎么定義goroutine
很簡單,在方法前面加上go就可以了
func main() {
go sayHello()
}
func sayHello() {
fmt.Println("hello")
}
也可以直接這樣寫, 基于匿名函式
go func() {
fmt.Println("hello")
}()
go 語言至少有一個main goroutine, 另外呼叫的sayhello goroutine和main goroutine并發執行,會在main goroutine退出后退出,所以我們上面的代碼是有問題的,它不會列印出"hello". 因為main goroutine退出了,它沒有機會執行,我們需要用到WaitGroup在main goroutine上面等待它,如下代碼
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("hello")
}()
wg.Wait()
wg.Add(1), 表示會執行一次, wg.Done就是說執行了一次, wg.Wait()會等待add的一次done.
main goroutine 可以和 goroutine共享相同的地址空間執行,如下代碼
var wg sync.WaitGroup
salutation := "hello"
wg.Add(1)
go func() {
defer wg.Done()
salutation = "welcome"
}()
wg.Wait()
fmt.Println(salutation)
會列印出“welcome”, salutation變數在goroutine中被改變后,也會在main goroutine中生效,
我們來看下面的代碼會輸出什么
var wg sync.WaitGroup
for _, salutation := range []string{"hello", "greetings", "good day"} {
wg.Add(1)
go func() {
defer wg.Done()
mt.Println(salutation)
}()
}
wg.Wait()
它會列印三個good day, 為什么呢? 作者的解釋是在goroutine開始之前回圈有很高的概率會退出,salutation的值不在范圍之內, go 語言運行時會足夠小心的將變數salutation值得參考仍然保留,由記憶體轉移到堆, 我不是特別明白作者得解釋,我自己理解下,感覺是for回圈是main goroutine, 它執行for很快,呼叫goroutine得時候它已經回圈完了,所以就拿到最后得值了,
這個我們想列印三個不同得值,使用下面得代碼
var wg sync.WaitGroup
for _, salutation := range []string{"hello", "greetings", "good day"} {
wg.Add(1)
go func(value string) {
defer wg.Done()
fmt.Println(value)
}(salutation)
}
wg.Wait()
這樣三個goroutine使用的是各自的副本,
goroutine的開銷是什么樣的呢?
一個goroutine占多少記憶體? 作者書上說大概是2.817KB, 我自己實驗了下我的機器上是9.072KB,而OS執行緒的一般會是2M, 差距還是有些大,所以我們說啟動百萬的goroutine也是很正常的,而執行緒一般幾十個就不錯了, 下面是示例代碼
func main() {
memConsumed := func() uint64 {
runtime.GC()
var s runtime.MemStats
runtime.ReadMemStats(&s)
return s.Sys
}
var c <-chan interface{}
var wg sync.WaitGroup
noop := func() {
wg.Done()
<-c
}
const numGoroutines = 5e4
wg.Add(numGoroutines)
before := memConsumed()
for i := numGoroutines; i > 0; i-- {
go noop()
}
after := memConsumed()
fmt.Printf("%.3fkb", float64(after-before)/numGoroutines/1000)
}
numGoroutines是我們想創建的goroutine數量
noop 這個方法一直等channel里面的value,它不會退出,創建了的goroutine一直在記憶體中,
memConsumed 方法統計當前的記憶體, 我們在開始的時候統計下,在結束的時候統計下,相減后就得到消耗的記憶體,
作者還列舉了goroutine的背景關系切換耗時225ns, 而執行緒切換1467ns, 相差也比較大,
最后
作者得出的結論是使用goroutine非常廉價
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/535166.html
標籤:其他
