可以很轻松的实现一个带缓冲的通道 (可以参见 14.2.5 章节),它的容量是并发请求的最大数目。下面的示例 max_tasks.go
没做任何事情,它包含了下列技巧:不超过 MAXREQS 的请求将被处理并且是同时处理,因为当通道 sem 的缓冲区全被占用时,函数 handle 被阻塞,直到缓冲区中的请求被执行完成并且从 sem 中删除之前,不能执行其他的请求。sem 就像一个 semaphore (信号量),表示一个在一定条件的程序中的一个标志变量的技术术语:由此得名。
Listing 14.16—max_tasks.go:
package main
const (
AvailableMemory = 10 << 20
// 10 MB, 示例
AverageMemoryPerRequest = 10 << 10
// 10 KB
MAXREQS = AvailableMemory / AverageMemoryPerRequest
// 原文中说 MAXREQS 是 1000,实际计算是 1024 ,后面按照原文的 1000 来描述
)
var sem = make(chan int, MAXREQS)
type Request struct {
a, b int
replyc chan int
}
func process(r *Request) {
// Do something 做任何事
// 可能需要很长时间并使用大量内存或CPU
}
func handle(r *Request) {
process(r)
// 信号完成:开始启用下一个请求
// 将 sem 的缓冲区释放一个位置
<-sem
}
func Server(queue chan *Request) {
for {
sem <- 1
// 当通道已满(1000 个请求被激活)的时候将被阻塞
// 所以停在这里等待,直到 sem 有容量(被释放),才能继续去处理请求
// (doesn’t matter what we put in it)
request := <-queue
go handle(request)
}
}
func main() {
queue := make(chan *Request)
go Server(queue)
}
通过这种方式,程序中的协程通过使用缓冲通道(这个通道作为一个 semaphore 被使用)来调整资源的使用,实现了对内存等有限资源的优化。