参考资料:
1. 无缓冲channel
【示例】在网球比赛中,两位选手会把球在两个人之间来回传递。选手总是处在以下两种状态之一,要么在等待接球,要么将球打向对方。可以使用两个 goroutine 来模拟网球比赛,并使用无缓冲的通道来模拟球的来回,代码如下所示。
// 这个示例程序展示如何用无缓冲的通道来模拟 // 2 个goroutine 间的网球比赛
package mainimport ("fmt" //标准输入输出"math/rand" //提供随机数"sync" //提供同步函数"time" //提供时间函数)var wg sync.WaitGroup //WaitGroup变量func init() { //总之,要想实现随机数,就得写这个rand.Seed(time.Now().UnixNano()) //设置随机数种子,保证“每次随机都是随机的”}func main() {court := make(chan int) //传递的球(击打次数)wg.Add(2) //表示需要等两个goroutine“Done”go player("Jin", court)go player("Tom", court)court <- 1 //“发球”,即给出第一次击打wg.Wait() //等待两个goroutine打完}func player(name string, court chan int) {defer wg.Done() //defer关键字是在函数要返回之前调用一些操作for {hit_num,ok := <- court //得到当前“球”和球的状态if !ok {//如果球没打过来,受球方赢fmt.Printf("player %s Won\n", name)return}n := rand.Intn(100) //生成随机数if n % 13 == 0 { //随便定义,例如此时,击球方missfmt.Printf("player %s Missed\n", name)//close(court) //“球掉了”,即关闭channel时它的状态为falsereturn}//以上都没发生,就正常击球,击球次数+1fmt.Printf("Player %s hit %d\n", name, hit_num)hit_num++court <- hit_num //更新channel的值传递给受球方}}
2. 有缓冲channel
package mainimport ("fmt")func main() {//make(chan 通道类型, 缓冲大小),这里的“缓冲大小”是可接收元素的数量ch := make(chan int, 4) //可接收 4 个元素fmt.Println(len(ch)) //输出:0ch <- 1ch <- 2ch <- 3ch <- 4fmt.Println(len(ch)) //输出:4le := len(ch) //输出:1 2 3 4for i:=0;i<le;i++ {fmt.Println(<-ch)}}
3. channel的超时机制:select
虽然 select 机制不是专门为超时而设计的,却能很方便的解决超时问题,因为 select 的特点是只要其中有一个 case 已经完成,程序就会继续往下执行,而不会考虑其他 case 的情况。
- 与 sw itch 语句相比,select 有比较多的限制,其中最大的一条限制就是每个 case 语句里必须是一个 IO 操作,大致的结构如下:
【示例】报数程序: ```go package mainselect {case <-chan1:// 如果chan1成功读到数据,则进行该case处理语句case chan2 <- 1:// 如果成功向chan2写入数据,则进行该case处理语句default:// 如果上面都没有成功,则进入default处理流程}
import ( “fmt” “time” )
func main() { ch := make(chan int) //报数 quit := make(chan bool) //结束信号 var t1 time.Time //声明一个时间类型变量
//开一个协程(匿名函数)go func() {for {select {case num := <-ch: //有数就报数fmt.Println("num = ", num)case <-time.After(3 * time.Second): //等待三秒,没有数就报错超时t2 := time.Now() //获取当前时间,t2.Sub(t1)是t2与t1的时间差fmt.Println("time out! (wait for", t2.Sub(t1), "seconds)")quit <- true //传递结束信号}}}()for i := 0; i < 5; i++ {time.Sleep(time.Second) //每次报数间隔一秒if i == 4 {t1 = time.Now() //获取最后一次报数的时间,用于计算时间差}ch <- i}<-quitfmt.Println("Program over.")
}
- 输出:
num = 0 num = 1 num = 2 num = 3 num = 4 time out! (wait for 3.000954194s seconds) Program over.
- 关于time.After(): 在 golang 中,谁也无法保证某些情况下的 select 是否会永久阻塞。很多时候都需要设置一下 select 的超时时间,可以借助 time 包的 After() 实现。---<a name="qScUZ"></a>## 4. 在业务中的使用举例:```govar wg sync.WaitGroupfor _, val := range arrars {wg.Add(1) //要做+1go func() {//...wg.Done() //做完1个}()}wg.Wait() //等待全部完成
