package main import ( "fmt" "runtime" "sync" ) const N = 26 func main() { const GOMAXPROCS = 1 runtime.GOMAXPROCS(GOMAXPROCS) var wg sync.WaitGroup wg.Add(N) for i := 0; i < N; i++ { go func(i int) { defer wg.Done() fmt.Println(i) }(i) } wg.Wait() }
25
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24html
package main import ( "fmt" "runtime" "sync" ) const N = 26 func main() { const GOMAXPROCS = 1 runtime.GOMAXPROCS(GOMAXPROCS) var wg sync.WaitGroup wg.Add(N) for i := 0; i < N; i++ { go func() { defer wg.Done() fmt.Println(i) }() } wg.Wait() }
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26
26併發
package main import ( "fmt" "runtime" "sync" ) const N = 26 func main() { const GOMAXPROCS = 1 runtime.GOMAXPROCS(GOMAXPROCS) var wg sync.WaitGroup wg.Add(4) for i := 0; i < N; i++ { go func(i int) { defer wg.Done() fmt.Println(i) }(i) } wg.Wait() }
25
0
1
2函數
package main import "fmt" func main() { for i:=0; i<10; i++ { go func() { fmt.Println(i) }() } }
無任何打印post
package main import ( "fmt" "runtime" "sync" ) const N = 26 func main() { const GOMAXPROCS = 1 runtime.GOMAXPROCS(GOMAXPROCS) var wg sync.WaitGroup wg.Add(2 * N) for i := 0; i < N; i++ { go func(i int) { defer wg.Done() fmt.Printf("%c", 'a'+i) }(i) go func(i int) { defer wg.Done() fmt.Printf("%c", 'A'+i) }(i) } go func() {}() wg.Wait() }
經過無緩衝的通道阻塞來實現控制goroutine的執行順序spa
unbuffered channel
無緩衝的通道
在接收前沒有能力保存任何值的通道
要求發送goroutine和接收goroutine同時準備好,才能完成發送和接收的操做
若是兩個goroutine沒有同時準備好,通道會致使先執行發送或接收操做的goroutine阻塞等待
這種對通道進行發送和接收的交互行爲自己就是同步的
其中任意一個操做都沒法離開另外一個操做單獨存在
Go基礎系列:指定goroutine的執行順序 - 駿馬金龍 - 博客園 https://www.cnblogs.com/f-ck-need-u/p/9994652.htmlcode
package main import ( "fmt" "time" ) func A(a, b chan struct{}) { <-a fmt.Println("A()!") close(b) } func B(a, b chan struct{}) { <-a fmt.Println("B()!") close(b) } func C(a chan struct{}) { <-a fmt.Println("C()!") } func main() { /* unbuffered channel 無緩衝的通道 在接收前沒有能力保存任何值的通道 要求發送goroutine和接收goroutine同時準備好,才能完成發送和接收的操做 若是兩個goroutine沒有同時準備好,通道會致使先執行發送或接收操做的goroutine阻塞等待 這種對通道進行發送和接收的交互行爲自己就是同步的 其中任意一個操做都沒法離開另外一個操做單獨存在 */ x := make(chan struct{}) y := make(chan struct{}) z := make(chan struct{}) go C(z) go B(y, z) go C(z) go A(x, y) go C(z) close(x) // 給打印留時間 time.Sleep(3 * time.Second) }
A()!
B()!
C()!
C()!
C()!htm
goroutine併發寫blog
package main import ( "math/rand" "sync" ) const N = 10 func main() { m := make(map[int]int) wg := &sync.WaitGroup{} wg.Add(N) for i := 0; i < N; i++ { go func() { defer wg.Done() m[rand.Int()] = rand.Int() }() } wg.Wait() println(len(m)) }
當N相對大時,好比10e4報錯遊戲
加鎖資源
同步訪問共享資源的方式之一
使用互斥鎖mutex
互斥鎖概念來自互斥(mutual excusion)概念
互斥鎖用於在代碼上建立一個臨界區,保證同一時間只有一個goroutine能夠執行這個臨界區代碼
《Go 語言實戰》
package main import ( "math/rand" "sync" ) const N = 100000 func main() { m := make(map[int]int) wg := &sync.WaitGroup{} var mutex sync.Mutex wg.Add(N) for i := 0; i < N; i++ { go func() { defer wg.Done() mutex.Lock() m[rand.Int()] = rand.Int() mutex.Unlock() }() } wg.Wait() println(len(m)) }
用無緩衝的通道來模擬2個goroutine間的網球比賽
package main import ( "fmt" "math/rand" "sync" "time" ) // 用來等待程序結束 var wg sync.WaitGroup func init() { rand.Seed(time.Now().UnixNano()) } func main() { // 建立一個無緩衝的通道 court := make(chan int) // 計數加2,表示要等待2個goroutine wg.Add(2) // 啓動2個選手 go player("A", court) go player("B", court) // 發球 court <- 1 // 等待遊戲結束 wg.Wait() } // player模擬一個選手在打網球 func player(name string, court chan int) { // 在函數退出時調用Done來通知main函數工做已經完成 defer wg.Done() for { // 等待球被擊打過來 ball, ok := <-court if !ok { // 若是通道關閉,咱們就贏了 fmt.Printf("Player %s Won\n", name) return } // 選隨機數,而後用這個數來判斷咱們是否丟球 n := rand.Intn(100) if n%13 == 0 { fmt.Printf("Player %s Missed\n", name) close(court) return } // 顯示擊球數,並將擊球數加1 fmt.Printf("Player %s Hit %d\n", name, ball) ball++ // 將球打向對手 court <- ball } }
Player B Hit 1
Player A Hit 2
Player B Hit 3
Player A Hit 4
Player B Hit 5
Player A Hit 6
Player B Hit 7
Player A Hit 8
Player B Missed
Player A Won
接力比賽
package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup func main() { // 建立一個無緩衝的通道 baton := make(chan int) // 爲最後一位跑步者將計數加1 wg.Add(1) // 第一位跑步者持有接力棒 go Runner(baton) // 開始比賽 baton <- 1 // 等待比賽結束 wg.Wait() } // Runner 模擬接力比賽中的一位跑步者 func Runner(baton chan int) { var newRunner int // 等待接力棒 runner := <-baton // 開始繞着跑道跑步 fmt.Printf("Runner %d Running With Baton\n", runner) // 建立下一位跑步者 if runner != 4 { newRunner = runner + 1 fmt.Printf("Runner %d To The Line\n", newRunner) go Runner(baton) } // 圍繞跑道跑 time.Sleep(100 * time.Millisecond) // 比賽結束了嗎? if runner == 4 { fmt.Printf("Runner %d Finished, Race over\n", runner) wg.Done() return } // 將接力棒交給下一位跑步者 fmt.Printf("Runner %d Exchange With Runner %d\n", runner, newRunner) baton <- newRunner }
Runner 1 Running With Baton
Runner 2 To The Line
Runner 1 Exchange With Runner 2
Runner 2 Running With Baton
Runner 3 To The Line
Runner 2 Exchange With Runner 3
Runner 3 Running With Baton
Runner 4 To The Line
Runner 3 Exchange With Runner 4
Runner 4 Running With Baton
Runner 4 Finished, Race over
package main import ( "fmt" "math/rand" "sync" "time" ) const ( numberGorutines = 4 // 要使用的goroutine的數量 taskLoad = 10 // 要處理的工做的數量 ) var wg sync.WaitGroup // init初始化包,Go語言運行時會在其餘代碼執行以前 // 優先執行這個函數 func init() { // 初始化隨機數種子 rand.Seed(time.Now().Unix()) } func main() { // 建立一個有緩衝的通道來管理工做 tasks := make(chan string, taskLoad) // 啓動goroutine來處理工做 wg.Add(numberGorutines) for gr := 1; gr <= numberGorutines; gr++ { go worker(tasks, gr) } // 增長一組要完成的工做 for post := 1; post <= taskLoad; post++ { tasks <- fmt.Sprintf("Task : %d", post) } // 當全部工做都處理完時關閉通道 // 以便全部goroutine退出 close(tasks) // 等待全部工做完成 wg.Wait() } // worker做爲goroutine啓動來處理 // 從有緩衝的通道傳入的工做 func worker(tasks chan string, worker int) { // 通知函數已經返回 defer wg.Done() for { // 等待分配工做 task, ok := <-tasks if !ok { // 這意味着通道已經空了,而且已被關閉 fmt.Printf("Worker: %d : Shutting Down\n", worker) return } // 顯示咱們開始工做了 fmt.Printf("Worker: %d : Started %s\n", worker, task) // 隨機等一段時間來模擬工做 sleep := rand.Int63n(100) time.Sleep(time.Duration(sleep) * time.Millisecond) // 顯示咱們完成了工做 fmt.Printf("Worker: %d : Completed %s \n", worker, task) } }
Worker: 4 : Started Task : 1
Worker: 1 : Started Task : 2
Worker: 2 : Started Task : 3
Worker: 3 : Started Task : 4
Worker: 2 : Completed Task : 3
Worker: 2 : Started Task : 5
Worker: 2 : Completed Task : 5
Worker: 2 : Started Task : 6
Worker: 2 : Completed Task : 6
Worker: 2 : Started Task : 7
Worker: 4 : Completed Task : 1
Worker: 4 : Started Task : 8
Worker: 2 : Completed Task : 7
Worker: 2 : Started Task : 9
Worker: 4 : Completed Task : 8
Worker: 4 : Started Task : 10
Worker: 1 : Completed Task : 2
Worker: 1 : Shutting Down
Worker: 2 : Completed Task : 9
Worker: 2 : Shutting Down
Worker: 3 : Completed Task : 4
Worker: 3 : Shutting Down
Worker: 4 : Completed Task : 10
Worker: 4 : Shutting Down
可以從已經關閉的通道接收數據這一點很是重要,由於這容許通道關閉後依舊可以取出其中緩衝的所有值,而不會有數據丟失。從一個已經關閉且沒有數據的通道里獲取數據,總會馬上返回,並返回一個通道類型的零值。