8.4 Go select
Go语言引入了select
关键字,用于处理异步IO问题,语义和switch
特别相似。语法由select开始,每个条件由case语句来描述。每个case语句必须是IO操作
。
select { case <-chan1: //如果读取到channel的数据,就执行这里 case chan2<-1: //如果成功向chan2写入数据,就执行这里 default: //上述都失败了,进入这里 }
案例
使用select可以解决从管道取数据的阻塞问题
package main import ( "fmt" "time" ) func main() { //定义一个管道 intChan := make(chan int, 10) //循环写入数据 for i := 0; i < 10; i++ { intChan <- i } //定义管道,可以写入string strChan := make(chan string, 5) for i := 0; i < 5; i++ { //格式化后写入string数据 strChan <- "hello" + fmt.Sprintf("%d", i) } //传统的for循环遍历,必须close关闭channel,否则造成死锁 //但是到底关闭哪个channel,并不那么容易选择 //加上for无线循环,匹配所有的case,直到结束return for { select { //如果有数据被读取到,进入这个分支,并且intChan没关闭的话,也会自动匹配下一个case case v := <-intChan: fmt.Printf("从intChan读取到数据%d\n", v) time.Sleep(time.Second) case v2 := <-strChan: fmt.Printf("从strChan中读取到数据%s\n", v2) time.Sleep(time.Second) default: fmt.Printf("什么也没读到,再见\n") time.Sleep(time.Second) return } } }
1.1.1. waitGroup等待组
package main import ( "fmt" "math/rand" "sync" "time" ) func main() { /* 同步等待组:WaitGourp,执行了wait的goroutine,要等待同步等待组中的其他的goroutine执行完毕。。 内置的计数器:counter:0 Add(),设置counter的值 Done(),将counter减一,同Add(-1) 以上两个方法可以设置counter的值,注意不能为负数,否则会引发恐慌。 Wait(),哪个goroutine执行了,那么就会被阻塞,直到counter为0。解除阻塞 */ var wg sync.WaitGroup //fmt.Printf("%T\n",wg) //fmt.Println(wg) wg.Add(2) go printNum1(&wg) go printNum2(&wg) wg.Wait() //main,进入阻塞状态,底层计数器为0,接触阻塞。。 //time.Sleep(1*time.Second) fmt.Println("main。。接触阻塞。。结束了。。。") } func printNum1(wg *sync.WaitGroup) { rand.Seed(time.Now().UnixNano()) for i := 1; i <= 100; i++ { fmt.Println("子goroutine1,i:", i) time.Sleep(time.Duration(rand.Intn(1000))) // } wg.Done() //计数器减一 } func printNum2(wg *sync.WaitGroup) { for j := 1; j <= 100; j++ { fmt.Println("\t子goroutine2,j:", j) time.Sleep(time.Duration(rand.Intn(1000))) } wg.Done() }