go之Effective Go中的客户请求处理程序示例导致死锁
del
阅读:60
2025-06-02 22:19:02
评论:0
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 goroutines产生了,它们都收听单个
clientRequests channel 。但是
clientRequests channel 仅用于一个请求,因此一旦请求进入,所有goroutine都可以访问同一请求。为什么这有用?
请您参考如下方法:
调用serve的代码不应与填充 channel 的代码在同一goroutine中运行。
在您的代码中,serve启动处理程序goroutine,但随后等待<-quit。由于已被阻止,因此您永远不会到达填充serveChannel的代码。因此, worker 永远不会有任何消耗。您也永远不会通知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 = ", 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 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)
}
至于您的最后一个问题:多个处理程序将看不到相同的请求。当一个处理程序从 channel 接收到一个值时,该值将从其中删除。下一个处理程序将接收下一个值。将 channel 视为可以并发使用的先进先出队列。
您可以在 the playground上找到代码的最后迭代。
声明
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。



