在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
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 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 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 package main import "fmt" func main() { for i:=0; i<10; i++ { go func() { fmt.Println(i) }() } } 无任何打印
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的执行顺序 unbuffered channel Go基础系列:指定goroutine的执行顺序 - 骏马金龙 - 博客园 https://www.cnblogs.com/f-ck-need-u/p/9994652.html
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()!
goroutine并发写 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
接力比赛 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
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
能够从已经关闭的通道接收数据这一点非常重要,因为这允许通道关闭后
goroutine https://tour.golang.org/concurrency/1 Goroutines run in the same address space, so access to shared memory must be synchronized. The
https://golang.org/doc/faq#closures_and_goroutines What happens with closures running as goroutines?Some confusion may arise when using closures with concurrency. Consider the following program: func main() { done := make(chan bool) values := []string{"a", "b", "c"} for _, v := range values { go func() { fmt.Println(v) done <- true }() } // wait for all goroutines to complete before exiting for _ = range values { <-done } } One might mistakenly expect to see To bind the current value of for _, v := range values { go func(u string) { fmt.Println(u) done <- true }(v) } In this example, the value of Even easier is just to create a new variable, using a declaration style that may seem odd but works fine in Go: for _, v := range values { v := v // create a new 'v'. go func() { fmt.Println(v) done <- true }() } This behavior of the language, not defining a new variable for each iteration, may have been a mistake in retrospect. It may be addressed in a later version but, for compatibility, cannot change in Go version 1.
闭包
https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.1.md 存在两种并发方式:确定性的(明确定义排序)和非确定性的(加锁/互斥从而未定义排序)。Go 的协程和通道理所当然的支持确定性的并发方式(例如通道具有一个 sender 和一个 receiver)。
|
请发表评论