golang ---------- channel 通道(三 select)

1、select是Go中的一个控制结构,类似于switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。

2、select中的case语句必须是一个channel操作,select中的default子句总是可运行的。

3、如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。

4、如果没有可运行的case语句,且有default语句,那么就会执行default的动作。

5、如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行

例子:

func generator() chan int {
	out := make(chan int)
	go func() {
		i := 0
		for{
			time.Sleep(time.Duration(rand.Intn(1000))*time.Millisecond)
			out <- i
			i++
		}
	}()
	return out
}

func work(id int, c chan int) {
	for n := range c {
		fmt.Printf("Worker %d received %d\n",
			id, n)
	}
}

func GoWork(id int) chan<- int {
	c := make(chan int)
	go work(id, c)
	return c
}

func main(){
	var c1,c2 = generator(),generator()   //c1,c2 ==nil
	w := GoWork(0)
	for{
		select {
		case n := <- c1:
			w <- n         //这样不好,收一个数之后,后面的操作又会阻塞,最后的出的结果是由顺序的,一个任务结束之后才可以进行下一个
		case n := <- c2:
			w <- n
		}
	}
}

例2:上面的优化:

1、不成功的优化:

func main(){
	var c1,c2 = generator(),generator()   //c1,c2 ==nil
	w := GoWork(0)
	for{
		n := 0
		select {
		case n = <- c1:
		case n = <- c2:
		case w <- n:      //这样w会一直收到0,因为c1,c2需要一段时间,所以就直接执行case w <- n

		}
	}
}

2、优化2:仍然存在生成数据与消耗数据速度不一致问题

func main(){
	var c1,c2 = generator(),generator()   //c1,c2 ==nil
	 w := GoWork(0)

	n := 0
	hasValue := false
	for{
		var activeWork chan <- int      //当activeWork是nil时,这里一直是block
		if hasValue {
			activeWork = w
		}

		select {
		case n = <- c1:
			hasValue = true
		case n = <- c2:
			hasValue = true
		case activeWork <- n:       //现在存在的问题是生成数据与消耗数据速度不一致,解决方法是将生成的数据保存下来排队
			hasValue = false
		}
	}
}
3、优化3 将生成的数据保存下来排队,生成的数都可以print
func main(){
	var c1,c2 = generator(),generator()   //c1,c2 ==nil
	 w := GoWork(0)

	var values []int
	for{
		var activeWork chan <- int    //最开始为nil,等有值才初始化
		var activeValue int
		if len(values) > 0 {
			activeWork = w
			activeValue = values[0]
		}

		select {
		case n := <- c1:
			values = append(values,n)
		case n := <- c2:
			values = append(values,n)
		case activeWork <- activeValue:      
			values = values[1:]
		}
	}
}

4、设置程序执行时间

func main(){
	var c1,c2 = generator(),generator()
	 w := GoWork(0)

	var values []int
	tm := time.After(10 * time.Second)      //10秒后会往tm这个channel中送一个时间,时间是多少无所谓
	for{
		var activeWork chan <- int      //最开始为nil,等有值才初始化
		var activeValue int
		if len(values) > 0 {
			activeWork = w
			activeValue = values[0]
		}

		select {
		case n := <- c1:
			values = append(values,n)
		case n := <- c2:
			values = append(values,n)
		case activeWork <- activeValue:             //当activeWork是nil时,这里一直是block 
			values = values[1:]

		case <- time.After(222 * time.Millisecond):    //两次select间隔的时间超过这个时间,会执行此case
			fmt.Println("两次select间隔的时间超时")

		case <- tm:              //10秒到了,从tm这个channel中接受到了数据,程序结束
			fmt.Println("时间到:over")
			return
		}
	}
}

5、查看values队列有多长

func main(){
	var c1,c2 = generator(),generator()
	 w := GoWork(0)

	var values []int
	tm := time.After(10 * time.Second)      //10秒后会往tm这个channel中送一个时间,时间是多少无所谓
	tick := time.Tick(time.Second)                  //定时,每个一段时间送一个值过来
	for{
		var activeWork chan <- int        //最开始为nil,等有值才初始化
		var activeValue int
		if len(values) > 0 {
			activeWork = w
			activeValue = values[0]
		}

		select {
		case n := <- c1:
			values = append(values,n)
		case n := <- c2:
			values = append(values,n)
		case activeWork <- activeValue:        //当activeWork是nil时,这里一直是block 
			values = values[1:]

		case <- tick:
			fmt.Println("values len",len(values))    //每一秒,测试一下队列的长度

		case <- time.After(222 * time.Millisecond):    //两次select间隔的时间超过这个时间,会执行此case
			fmt.Println("两次select间隔的时间超时")
			//return

		case <- tm:              //10秒到了,从tm这个channel中接受到了数据,程序结束
			fmt.Println("时间到:over")
			return
		}
	}
}

你可能感兴趣的:(golang ---------- channel 通道(三 select))