在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
并发与并行的区别: go fmt.PrintLn("hello") 关键字go并非执行并发操作,而是创建一个并发任务单元。 package main import ( "fmt" "time" ) var c int //初始为0 func counter() int { c++ return c } func main() { a := 100 go func(x, y int) { //匿名函数直接执行 //hour min second 时分秒 time.Sleep(time.Second) //休息一秒钟 fmt.Println("go", x, y) //执行counter 100 1 //goroutine在main之后执行 }(a, counter()) //立即计算并复制参数 a += 100 fmt.Println("main:", a, counter()) //200 2 time.Sleep(time.Second * 3) //等待goroute结束 } /* main: 200 2 go 100 1 为什么先打印main? go创建一个并发单元,但是不会马上执行,而是会放置在队列中 */
进程退出时不会等待并发任务结束,可用通道(channel)阻塞,然后发出退出信号。 package main import ( "fmt" "time" ) func main() { // exit := make(chan struct{}) //创建通道。因为仅仅是通知,数据并没有实际意义。 go func() { time.Sleep(time.Second) fmt.Println("goroutine done.") // close(exit) //关闭通道发出信号 }() fmt.Println("main...") // <-exit //如通道关闭,立即解除阻塞。 fmt.Println("main exit...") }
<-exit接收操作符,如果exit代表了元素类型为byte的通道类型值,则此表达式就表示从exit中接收一个byte类型值的操作。 package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) //累加计数,每循环以此加一 go func(id int) { //创建多个任务单元 defer wg.Done() //递减计数,这个任务计数之前减一 time.Sleep(time.Second) fmt.Println("goroutine", id, "done") }(i) } fmt.Println("main...") wg.Wait() //阻塞,直至wg归零 fmt.Println("main exit") } /* main... goroutine 6 done goroutine 1 done goroutine 2 done goroutine 4 done goroutine 9 done goroutine 8 done goroutine 0 done goroutine 7 done goroutine 5 done goroutine 3 done main exit */
尽管WaitGroup.Add实现了原子操作,但建议在goroutine外累加计数器,以免Add尚未执行,wait已经退出。 go func(id int) { wg.Add(1) defer wg.Done() }
可在多处使用wait阻塞,它们都可以接收到通知。 package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup wg.Add(1) go func() { wg.Wait() fmt.Println("wait exit") }() go func() { time.Sleep(time.Second * 3) fmt.Println("done.") wg.Done() }() wg.Wait() fmt.Println("main exit.") } /* done. wait exit main exit. */
GOMAXPROCS 线程数量 package main import ( "fmt" "math" "runtime" "sync" "time" ) //测试目标函数 func count() { x := 0 for i := 0; i < math.MaxUint32; i++ { x += 1 } fmt.Println(x) } //循环执行 func test(n int) { for i := 0; i < n; i++ { count() } } //并发执行 func test2(n int) { var wg sync.WaitGroup wg.Add(n) //计数为4 for i := 0; i < n; i++ { go func() { count() wg.Done() }() } wg.Wait() } func main() { n := runtime.GOMAXPROCS(0) //4 fmt.Println(time.Now()) // test(n) //6s test2(n) //3s fmt.Println(time.Now()) }
Local Storage 局部存储器 package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup var gs [5]struct { //使用数组来进行局部存储 id int //编号 result int //返回值 } fmt.Println(gs) for i := 0; i < len(gs); i++ { wg.Add(1) go func(id int) { defer wg.Done() gs[id].id = id gs[id].result = (id + 1) * 100 }(i) } wg.Wait() fmt.Printf("%+v\n", gs) } /* [{0 0} {0 0} {0 0} {0 0} {0 0}] [{id:0 result:100} {id:1 result:200} {id:2 result:300} {id:3 result:400} {id:4 result:500}] */
如果使用map作为局部存储容器,建议做同步处理,因为运行时会对其做并发读写检查。 package main import ( "fmt" "runtime" ) func main() { runtime.GOMAXPROCS(1) exit := make(chan struct{}) go func() { /任务a defer close(exit) go func() { //任务b fmt.Println("b") }() for i := 0; i < 4; i++ { fmt.Println("a:", i) if i == 1 { runtime.Gosched() //让出当前线程,调度执行b } } }() <-exit } /* a: 0 a: 1 b //这个b有可能打印不出来 a: 2 a: 3 */
该函数很少被使用,因为运行时会主动向长时间运行的任务发出抢占调度。 Goexit
package main import ( "fmt" "runtime" ) func main() { exit := make(chan struct{}) go func() { defer close(exit) defer fmt.Println("a") //执行3 func() { defer func() { fmt.Println("b", recover() == nil) //defer总会执行 2 }() func() { fmt.Println("c") //执行1 runtime.Goexit() //立即终止当前任务 fmt.Println("c done.") //不会执行 }() fmt.Println("b done.") //不会执行 }() fmt.Println("a done.") //不会执行 }() <-exit fmt.Println("main exit") //主程序 } /* c b true a main exit */
如果在main里调用Goexit,它会等其它任务结束,然后让进程直接崩溃。 package main import ( "fmt" "runtime" "time" ) func main() { for i := 0; i < 2; i++ { go func(x int) { for n := 0; n < 2; n++ { fmt.Printf("%c: %d\n", 'a'+x, n) time.Sleep(time.Millisecond) } }(i) } runtime.Goexit() //等待所有任务结束 fmt.Println("main exit.") } /* b: 0 a: 0 a: 1 b: 1 fatal error: no goroutines (main called runtime.Goexit) - deadlock! */
无论身处那一层,Goexit都能立即终止整个调用堆栈,这与return仅退出当前函数不同。 |
请发表评论