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上找到代码的最后迭代。
关于go - Effective Go中的客户请求处理程序示例导致死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63523648/