10.1.goroutine
goroutine的使用
//Learn_Go/main.go
package main
import (
"fmt"
"time"
)
func demo(count int) {
for i :=1; i < 10; i++{
fmt.Println(count,":",i)
}
}
func main() {
for i :=1; i < 10; i++{
go demo(i)
}
//添加休眠時間等待goroutine執行結束
time.Sleep(3e9)
}
10.2.waitgroup
WaitGroup直譯為等待組,其實就是計數器,只要計數器中有內容將一直阻塞
WaitGroup有三種方法
- Add(delta int)表示向內部計數器添加增量(delta),其中參數delta可以使負數
- Done() 表示減少waitgroup計數器的值,應當在程序最后執行,相當於Add(-1)
- Wait() 表示阻塞知道waitgroup計數器為0
//Learn_Go/main.go
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++{
go func(j int) {
fmt.Println("第",j,"次執行")
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("程序結束")
}
10.3.互斥鎖和讀寫鎖
(1)互斥鎖
可以使用sync.Mutex對內容加鎖,互斥鎖的使用場景
- 多個gouroutine訪問同一個函數代碼段
- 操作一個全局變量
- 為了保證共享變量安全性,值安全性
(2)讀寫鎖
Go語言中的map不是線程安全的,多個gouroutine同時操作會出現錯誤
RWMutex可以添加多個讀鎖或者一個寫鎖,讀寫鎖不能同時存在
map在並發下讀寫就需要結合讀寫鎖完成
互斥鎖表示鎖的代碼同一時間只能有一個goroutine運行,而讀寫鎖表示在鎖范圍內數據的讀寫操作
//Learn_Go/main.go
package main
import (
"fmt"
"sync"
)
func main() {
var rwm sync.RWMutex
var wg sync.WaitGroup
wg.Add(10)
m := make(map[int]int)
for i := 0; i < 10; i++{
go func(j int) {
rwm.Lock()
m[j] = j
fmt.Println(m)
rwm.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("程序結束")
}
10.4.channel
channel是進程內通信方式,每個channel只能傳遞一個類型的值,這個類型需要在聲明channel時指定
channel在Go中主要的兩個作用:同步和通信
(1)聲明channel的語法
- var 名稱 chan 類型
- var 名稱 chan <- 類型 只寫
- var 名稱 <- chan 類型 只讀
- 名稱 := make(chan int) 無緩存chanel
- 名稱 := make(chan int) 無緩存channel
- 名稱 := make(chan int,100) 有緩存channel
(2)操作channel的語法
- ch <- 值 向ch中添加一個值
- <- ch 從ch中取出一個值
- a := <-ch 從ch中取出一個值並賦值給a
- a,b := <-ch 從ch中取出一個值賦值給a,如果ch已經關閉或ch中沒有值,b為false,
(3)無論是向channel存數據還是取數據都會阻塞
//Learn_Go/main.go
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
fmt.Println("執行")
ch <- 111
}()
a := <- ch
fmt.Println(a)
fmt.Println("程序結束")
}
(4)使用channel實現gouroutine之間通信
//Learn_Go/main.go
package main
import "fmt"
func main() {
ch1 := make(chan string)
ch2 := make(chan int)
go func() {
ch1 <- "derek"
ch2 <- 111
}()
go func() {
content := <- ch1
fmt.Println("取出數據:",content) //取出數據: derek
ch2 <- 222
}()
a := <- ch2
b := <- ch2
fmt.Println(a,b) //111 222
fmt.Println("程序結束")
}
(5)可以使用for range獲取channel中內容
//Learn_Go/main.go
package main
import "fmt"
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
for i := 0; i<10;i++{
ch1 <- i
}
ch2 <- 222
}()
go func() {
for n := range ch1{
fmt.Println(n)
}
}()
<- ch2
fmt.Println("程序結束")
}
10.5.select
select執行過程
- 每個case必須是一個IO操作
- 哪個case可以執行就執行哪個
- 所有case都不能執行時,執行default
- 所有case都不能執行且沒有default,將會阻塞
//Learn_Go/main.go
package main
import "fmt"
func main() {
ch1 := make(chan int,2) //有緩存的channel
ch2 := make(chan int,3)
ch1 <- 111
ch2 <- 222
select {
case a := <-ch1:
fmt.Println(a)
case b := <-ch2:
fmt.Println(b)
default:
fmt.Println("錯誤")
}
}
select多和for循環結合使用
//Learn_Go/main.go
package main
import "fmt"
func main() {
ch := make(chan int)
for i := 0; i < 10;i++{
go func(j int) {
ch <- j
}(i)
}
//用for循環一直接受
for {
select {
case a := <- ch:
fmt.Println(a)
default:
}
}
}
