参考资料:
1. 无缓冲channel
【示例】在网球比赛中,两位选手会把球在两个人之间来回传递。选手总是处在以下两种状态之一,要么在等待接球,要么将球打向对方。可以使用两个 goroutine 来模拟网球比赛,并使用无缓冲的通道来模拟球的来回,代码如下所示。
// 这个示例程序展示如何用无缓冲的通道来模拟 // 2 个goroutine 间的网球比赛
package main
import (
"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 { //随便定义,例如此时,击球方miss
fmt.Printf("player %s Missed\n", name)
//
close(court) //“球掉了”,即关闭channel时它的状态为false
return
}
//以上都没发生,就正常击球,击球次数+1
fmt.Printf("Player %s hit %d\n", name, hit_num)
hit_num++
court <- hit_num //更新channel的值传递给受球方
}
}
2. 有缓冲channel
package main
import (
"fmt"
)
func main() {
//make(chan 通道类型, 缓冲大小),这里的“缓冲大小”是可接收元素的数量
ch := make(chan int, 4) //可接收 4 个元素
fmt.Println(len(ch)) //输出:0
ch <- 1
ch <- 2
ch <- 3
ch <- 4
fmt.Println(len(ch)) //输出:4
le := len(ch) //输出:1 2 3 4
for 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
}
<-quit
fmt.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. 在业务中的使用
举例:
```go
var wg sync.WaitGroup
for _, val := range arrars {
wg.Add(1) //要做+1
go func() {
//...
wg.Done() //做完1个
}()
}
wg.Wait() //等待全部完成