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
可以从已经关闭的通道接收数据这一点很是重要,由于这容许通道关闭后依旧可以取出其中缓冲的所有值,而不会有数据丢失。从一个已经关闭且没有数据的通道里获取数据,总会马上返回,并返回一个通道类型的零值。