注意事项
- goroutine只是官方的超级线程池
- 高并发性:占用内存小,创建销毁很快
- goroutine的简单易用,也在语言层面上给予开发者巨大的便利
- 并发不是并行,并行是直接利用多核实现多线程的运行,并发是由切换时间片来实现“同时”运行
-
goroutine奉行通过通信(channel)来共享内存,而不是共享内存来通信
channel
- channel是goroutine沟通的桥梁,大都是阻塞同步的
- 通过make创建,close关闭
channel是引用类型,也就是说,传入的参数可以直接对他本身操作
func main(){ c := make(chan bool) go func(){ fmt.Println("GO GO GO") c <- true }() <- c } /* > Output: command-line-arguments GO GO GO */
- 可以使用for range来迭代不断操作channel
- 迭代的时候记得一定要在某个地方调用close(c)关闭,以避免造成死锁
func main(){ c := make(chan bool) go func(){ fmt.Println("GO GO GO") c <- true close(c) }() for v := range c { //程序运行到这,一直等待着c会有值 fmt.Println(v) } }
- 可以设置单向或者双向通道
-
可以设置缓存大小,在未被填满前不会发生阻塞
关于缓存,可参考:Go语言_并发篇
有缓存是异步的,无缓存是同步阻塞的
有缓存要先放
无缓存要先取乱序及缺失问题..whatever
func main(){ runtime.GOMAXPROCS(runtime.NumCPU()) c := make(chan bool) for i:=0; i<10 ; i++ { go Go(c,i) } <-c } func Go(c chan bool,index int){ a := 1 for i:=0;i<1000000;i++{ a += i } fmt.Println(index,a) if index==9 { c <- true } } /* > Output: command-line-arguments 1 499999500001 0 499999500001 2 499999500001 9 499999500001 */
以上输出十分不符合圣意,大臣们献出以下两个计策
计策一:给channel加buffer
func main(){ runtime.GOMAXPROCS(runtime.NumCPU()) c := make(chan bool,10) for i:=0; i<10; i++ { go Go(c,i) } for i:=0;i<10;i++{ <- c } } func Go(c chan bool,a) c <- true } /* > Output: command-line-arguments 2 499999500001 1 499999500001 9 499999500001 0 499999500001 6 499999500001 3 499999500001 5 499999500001 7 499999500001 8 499999500001 4 499999500001 */
计策二:引入sync包
package main import ( "fmt" "runtime" "sync" ) func main(){ //(runtime包是goroutine的调度器),runtime.GOMAXPROCE设置允许同时最多使用多少个核 runtime.GOMAXPROCS(runtime.NumCPU()) wg := sync.WaitGroup{} for i:=0; i<10; i++ { //waitgroup作为结构,使用&进行地址传递 go Go(&wg,i) } wg.Wait() } func Go(wg *sync.WaitGroup,a) wg.Done() }
select
- 专门为了channel设计的结构
func main(){ c1,c2 := make(chan int),make(chan string) o := make(chan bool) go func(){ for{ select{ case v,ok := <-c1: if !ok{ o <- true break } fmt.Println("c1",v) case v,ok := <-c2: if !ok{ o <- true break } fmt.Println("c2",v) } } }() c1 <- 1 c2 <- "hi" c1 <- 3 c2 <- "lel" close(c1) //close(c2) for i:=0;i<2;i++{ <- o } } //没办法同时记录两个select的执行次数
- 可以处理一个或者多个channel的发送与接收
func main(){ c := make(chan int) go func(){ i := 0 for v:= range c { i ++ fmt.Println(v) if i>10{ break } } }() for { select { case c <- 0: case c <- 1: } } } //运行结果很鬼畜,,
- 同时有多个可用的channel时按随机顺序处理
- 可用空的select来阻塞main函数
- 可设置超时
package main import ( "fmt" "time" ) func main(){ c := make(chan bool) select { case v := <- c: fmt.Println(v) case <-time.After(3*time.Second): fmt.Println("timeout") } } //time.After()返回一个time型的chan
例子:用goroutine实现发送接收数条消息
package main import ( "fmt" ) var cc chan string func main() { cc = make(chan string) go Go() for i := 0; i < 5; i++ { cc <- fmt.Sprintf("From main:hello,#%d",i) fmt.Println(<-cc) } } func Go() { for i := 0; ; i++ { fmt.Println("djd",<-cc) cc <- fmt.Sprintf("From Go:hi,i) } } /* djd From main:hello,#0 From Go:hi,#0 djd From main:hello,#1 From Go:hi,#1 djd From main:hello,#2 From Go:hi,#2 djd From main:hello,#3 From Go:hi,#3 djd From main:hello,#4 From Go:hi,#4 */