通道是用来传递数据的一个数据结构,Go语言中提倡用通信的方式来替代共享内存。当一个资源需要在协程之间共享时,通道会在goroutine之间建立一个管道,并提供了确保同步交换数据的机制。go中保证了在任何时候,同时只能有一个goroutine同时访问通道进行发送和获取数据。通道属于一种队列的数据结构遵循“先进先出”的规则。
需要注意的是channel的传递是传引用,在使用前需要make分配内存。如果要使用一个带缓冲的通道,需要提前设置通道的cap。如果一个goroutine在读取一个没有数据的通道时,会阻塞,直到有数据到来。
基础方法
示例:
package main
import "fmt"
func receive(c chan string) {
for {
message := <-c
fmt.Println("receive message:", message)
}
}
func main() {
c := make(chan string)
go receive(c)
c <- "hello"
close(c)
}
检查通道长度和容量
可以使用内置的cap()和len()函数来查看一个通道的容量和当前长度。但是只有一些特定场景才会使用。
场景1:一个routine要将一个未关闭的并且不会再向其发送数据的缓冲通道的所有数据接收出来,在确保只有这一个goroutine从此通道接收数据的情况下,可以通过下面的代码来实现:
for len(c) > 0{
value := <- c
//...
}
场景2:一个goroutine要将一个缓冲通道写满,而又不阻塞,在确保只有此一个goroutine向此通道发送数据的情况下,可以通过以下代码实现:
for len(c) < cap(c){
c <- value
}
速率限制
速率限制通常用来限制吞吐量和确保一段时间内的资源不会超标,例如下面示例中,任何1min内处理的请求数不会超过100个:
package main
import (
"fmt"
"time"
)
type request interface{}
func handle(r request) {
fmt.Println("handle request")
}
const RateLimitPeriod = time.Minute
const RateLimit = 100
func handleRequest(requests <-chan request) {
quatos := make(chan time.Time, RateLimit)
go func() {
tick := time.NewTicker(RateLimitPeriod / RateLimit) //处理一个请求的时间
defer tick.Stop()
for t := range tick.C {
for {
select {
case quatos <- t: //处理一个请求的时间到,则通知处理协程
default:
}
}
}
}()
for r := range requests {
<-quatos
handle(r)
}
}
func main() {
requests := make(chan request)
go handleRequest(requests)
for i := 0; ; i++ {
requests <- i
}
}
上面的例子中,设定好1min内最多处理100个请求,如果来了多于100个请求,那么handleRequest中也只会按照1min处理100个速率来处理,因为定时器未到期前,quatos通道只能阻塞等待,而无法handle(r).