天天看點

GoLang 高并發 Goroutine(一)并發和并行Goroutine

GoLang 高并發 Goroutine(一)

  • 并發和并行
  • Goroutine
    • goroutine 是如何工作的
    • fork-join并發模型
    • 閉包
      • 例1
      • 例2

并發和并行

并發和并行:并發屬于代碼,并行屬于一個運作的程式;具體是我們并沒有編寫并行的代碼,而是期待在運作時能夠并行的并發代碼

Goroutine

goroutine 是一個并發函數(并不是并行)

func main(){
	go sayHello()
}
func sayHello(){
	fmt.Println("你好")
}

/*
使用匿名函數
func main(){
	go func(){
		fmt.Println("你好")
	}()
}
*/
           

goroutine 是如何工作的

goroutine 既不是os線程,也不是由語言運作時管理的線程,它是一個協程(協程是一個非搶占式的簡單并發子goroutine(函數、閉包或方法))。 goroutine沒有定義自己的暫停方法和在運作點,Go語言在運作時會觀察goroutine的運作狀态,并在它們阻塞時自動挂起它們,然後在它們不阻塞時恢複它們。

fork-join并發模型

fork指在程式中的任意一點,可以将執行的子分支與其父節點同時運作;join在将來的某個時候,這些并發的執行分支将會合并在一起。

func main(){
	sayHello := func(){
		fmt.Println("你好")
	}
	go sayHello()
}
           

在這個例子中,沒有join點,執行sayHello()的goroutine将在未來某個不确定的時間退出,而程式的其餘部分将會繼續執行;因為不确定sayHello函數是否會執行,具體而言goroutine将會被建立,但是可能在還沒有執行,此時main goroutine已經退出結果是:可能看不到在螢幕上列印出 你好。有一種比較笨且沒有任何實際意義的方法,就是在建立goroutine之後,使用time.Sleep讓main goroutine睡眠一會,這種方式并不能保證結果的正确性,隻是提供了一種讓結果趨近于正确的方式。

是以要建立一個join使得main goroutine 和 sayHello goroutine 同步。

var wg sync.WaitGroup
sayHello := func(){
	defer wg.Done()
	fmt.Println("你好")
}
wg.Add(1)
go sayHello()
wg.Wait() // 連接配接點,main goroutine等待直到所有wg Done()
           

閉包

**閉包** 可以從建立它們的作用域擷取變量;思考:如果我們在goroutine上運作一個閉包,那麼閉包是在這些變量的副本上運作,還是原值的引用?
           

例1

var wg sync.WaitGroup
strs := "你好"
wg.Add(1)
go func(){
	defer wg.Done()
	strs = "哈哈" //修改了變量strs的值
}()
wg.Wait()
fmt.Println(strs) //哈哈
           

goroutine在它們所建立的相同位址空間内執行

例2

var wg sync.WaitGroup
for _ , str := range []string{"你好1","你好2","你好3"}{
	wg.Add(1)
	go func(){
		defer wg.Done()
		fmt.Println(str)
	}()
}

/*
你好3
你好3
你好3
*/
           

該閉包在使用str的時候,字元串的疊代已經結束了。因為goroutine可能在任何時間點運作,是以不确定在goroutine中會列印什麼值。還有一個問題是,如果在疊代結束後,str的值還在不在記憶體中,goroutine還能不能引用已經超出範圍的内容。這涉及到Go語言運作時會對變量str的值的引用仍然保留,有記憶體轉移到堆,是以goroutine仍然可以繼續通路它。

var wg sync.WaitGroup
for _ , str := range []string{"你好1","你好2","你好3"}{
	wg.Add(1)
	go func(s string){
		defer wg.Done()
		fmt.Println(s) //建構一個str的副本
	}(str)
}
/*
你好3
你好1
你好2
*/
           

由于多個goroutine可以在同一個位址空間上運作,是以我們要考慮資源競争(同步問題)。