并发中的select

select的执行顺序

  1. select语句不使用default分支时,处于阻塞状态直到其中一个channel的收/发操作准备就绪(或者channel关闭或者缓冲区有值),如果同时有多个channel的收/发操作准备就绪(或者channel关闭)则随机选择其中一个。
  2. select语句使用default分支时,处于非阻塞状态,从所有准备就绪(或者channel关闭或者缓冲区有值)的channel中随机选择其中一个,如果没有则执行default分支。

严格来讲,select的实现包括两种顺序:一是:轮询顺序(poll order),二是加锁/解锁顺序(lock order)。个人理解,这道题的本意应该是在问轮询顺序。

轮询顺序的实现: 首先将所有case分支进行随机排序,然后按照这个随机顺序来遍历case分支,选择第一个符合条件(就绪或关闭或缓冲区有值)的channel即返回不再遍历后面的case分支。

select/switch中的break

在 for switch 和 for select 的 block中,如果用了break
break出的是switch 和 select ,并不是break for
解决方法:
1 用 return
2 用 break tag

package main
import "fmt"

func main() {  
    loop:
        for {
            switch {
            case true:
                fmt.Println("breaking out...")
                break loop
            }
        }
    fmt.Println("out!")
}

示例

不使用default

package main
 
import (
	"fmt"
	"time"
)
 
func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	var cnt [2]int
	for i := 0; i < 1000; i++ {
		go func(c1 chan int) {
			c1 <- 0
		}(ch1)
 
		go func(c2 chan int) {
			c2 <- 1
		}(ch2)
 
		//等10毫秒,确保两个channel都已准备就绪
		time.Sleep(10 * time.Millisecond)
 
		var index int
 
		select {
		case index = <-ch1:
			cnt[index]++
		case index = <-ch2:
			cnt[index]++
		}
	}
	fmt.Printf("cnt=%v\n", cnt)
}

Out:

cnt=[497 503]

可以看出总和为1000,而且比例接近1:1(随机)

使用default

package main
 
import (
	"fmt"
	"time"
)
 
func main() {
	var cnt [2]int
	for i := 0; i < 1000; i++ {
		timer1 := time.NewTimer(5 * time.Millisecond)
		defer timer1.Stop()
 
		timer2 := time.NewTimer(5 * time.Millisecond)
		defer timer2.Stop()
 
		//等10毫秒,确保两个channel都已准备就绪,如果没有等待,则直接执行default分支,最后结果为[0 0]
		time.Sleep(10 * time.Millisecond)
 
		select {
		case <-timer1.C:
			cnt[0]++
		case <-timer2.C:
			cnt[1]++
		default:
		}
	}
 
	fmt.Printf("cnt=%v\n", cnt)
}

Out:

cnt=[512 488]

由于等待时间的存在,select还是在两个分支中进行随机选择,不走default流程;但是如果去掉等待时间,将变为

cnt=[0 0]

另一个例子

package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c<-x:
			x, y=y,x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c:=make(chan int)
	quit:=make(chan int)
	go func() {
		for i:=0;i<10;i++ {
			fmt.Println(<-c)
		}
		quit<-0
	}()    // 无缓存channel通信必须要在不同的goroutine中,否则无法满足一个产生一个消费的同步原则
	fibonacci(c,quit)
}

Out:

1
1
2
3
5
8
13
21
34
55
quit

超时退出机制

package main

import "time"
import . "fmt"

func main() {
    c := make(chan int)
    o := make(chan bool)
    go func() {
		for i:=0;i<100;i++ {
			c<-i
		}
	}()
    go func() {
        for {
            select {
                case v := <- c:
                    Println(v)
                case <- time.After(5 * time.Second):
                    Println("timeout")
                    o <- true
            }
        }
    }()
    <- o
}

该程序输出0-99,过5s后打印出timeout

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值