问题描述
Effective Go指南包含以下示例,用于处理客户请求:
func handle(queue chan *Request) {
for r := range queue {
process(r)
}
}
func Serve(clientRequests chan *Request,quit chan bool) {
// Start handlers
for i := 0; i < MaxOutstanding; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}
我在本地运行了类似的代码,其中客户端请求只是整数:
func handle(queue chan int) {
for r := range queue {
fmt.Println("r = ",r)
}
}
func serve(clientRequests chan int,quit chan bool) {
// Start handlers
for i := 0; i < 10; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}
var serveChannel = make(chan int)
var quit = make(chan bool)
serve(serveChannel,quit)
for i := 0; i < 10; i++ {
serveChannel <- i
}
但是我的代码导致死锁错误fatal error: all goroutines are asleep - deadlock!
。
即使从概念上我不理解程序中的问题,我也不了解原始代码的工作原理。我确实知道会生成MaxOutstanding
个goroutine,它们都收听单个clientRequests
通道。但是clientRequests
通道仅用于一个请求,因此一旦一个请求进入,所有goroutine都可以访问同一请求。为什么这样有用?
解决方法
调用serve
的代码不应与填充通道的代码在同一goroutine中运行。
在您的代码中,serve
启动处理程序goroutine,然后等待<-quit
。由于已被阻止,因此您永远不会到达填充serveChannel
的代码。因此,工人永远不会有任何消耗。您也永远不会通知quit
,而让serve
永远等待。
第一步是在单独的goroutine中将数据发送到serveChannel
。例如:
func handle(queue chan int) {
for r := range queue {
fmt.Println("r = ",r)
}
}
func serve(clientRequests chan int,quit chan bool) {
// Start handlers
for i := 0; i < 10; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}
func populateRequests(serveChannel chan int) {
for i := 0; i < 10; i++ {
serveChannel <- i
}
}
func main() {
var serveChannel = make(chan int)
var quit = make(chan bool)
go populateRequests(serveChannel)
serve(serveChannel,quit)
}
我们现在已根据需要处理所有请求。
但是,一旦处理完成,您仍然会遇到all goroutines are asleep
。这是因为serve
最终等待着quit
信号,但是没有发送信号。
在正常程序中,quit
将在捕获到信号或某些shutdown
请求之后被填充。由于我们什么都没有,因此只需三秒钟即可关闭它,也可以在单独的goroutine中关闭它。
func handle(queue chan int) {
for r := range queue {
fmt.Println("r = ",quit chan bool) {
// Start handlers
for i := 0; i < 10; i++ {
go handle(clientRequests)
}
<-quit // Wait to be told to exit.
}
func populateRequests(serveChannel chan int) {
for i := 0; i < 10; i++ {
serveChannel <- i
}
}
func quitAfter(quit chan bool,duration time.Duration) {
time.Sleep(duration)
quit <- true
}
func main() {
var serveChannel = make(chan int)
var quit = make(chan bool)
go populateRequests(serveChannel)
go quitAfter(quit,3*time.Second)
serve(serveChannel,quit)
}
关于您的最后一个问题:多个处理程序将看不到相同的请求。当一个处理程序从通道接收到一个值时,该值将从其中删除。下一个处理程序将接收下一个值。将通道视为可以并发使用的先进先出队列。
您可以在the playground上找到代码的最后迭代。