猿问

Effective Go 中的客户端请求处理程序示例导致死锁?

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单通道。但是clientRequests通道只针对一个请求,所以一旦一个请求进来,那么所有的 goroutine 都可以访问同一个请求。为什么这很有用?


慕无忌1623718
浏览 113回答 1
1回答

三国纷争

调用的代码serve不应该与填满通道的代码在同一个 goroutine 中运行。在您的代码中,serve启动处理程序 goroutines 但随后等待<-quit. 由于它被阻止,您永远无法访问填充的代码serveChannel。所以工人们从来没有任何东西可以消费。你也永远不会通知quit,让serve等待永远。第一步是serveChannel在单独的 goroutine 中发送数据。例如:func handle(queue chan int) {&nbsp; &nbsp; for r := range queue {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("r = ", r)&nbsp; &nbsp; }}func serve(clientRequests chan int, quit chan bool) {&nbsp; &nbsp; // Start handlers&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; go handle(clientRequests)&nbsp; &nbsp; }&nbsp; &nbsp; <-quit // Wait to be told to exit.}func populateRequests(serveChannel chan int) {&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; serveChannel <- i&nbsp; &nbsp; }}func main() {&nbsp; &nbsp; var serveChannel = make(chan int)&nbsp; &nbsp; var quit = make(chan bool)&nbsp; &nbsp; go populateRequests(serveChannel)&nbsp; &nbsp; serve(serveChannel, quit)}我们现在可以根据需要处理所有请求。all goroutines are asleep但是,一旦处理完成,您仍然会遇到。这是因为serve最终等待quit信号但没有任何东西可以发送。在正常程序中,quit将在捕获信号或某些shutdown请求后填充。由于我们没有任何东西,我们将在三秒钟后将其关闭,也在一个单独的 goroutine 中。func handle(queue chan int) {&nbsp; &nbsp; for r := range queue {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("r = ", r)&nbsp; &nbsp; }}func serve(clientRequests chan int, quit chan bool) {&nbsp; &nbsp; // Start handlers&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; go handle(clientRequests)&nbsp; &nbsp; }&nbsp; &nbsp; <-quit // Wait to be told to exit.}func populateRequests(serveChannel chan int) {&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; serveChannel <- i&nbsp; &nbsp; }}func quitAfter(quit chan bool, duration time.Duration) {&nbsp; &nbsp; time.Sleep(duration)&nbsp; &nbsp; quit <- true}func main() {&nbsp; &nbsp; var serveChannel = make(chan int)&nbsp; &nbsp; var quit = make(chan bool)&nbsp; &nbsp; go populateRequests(serveChannel)&nbsp; &nbsp; go quitAfter(quit, 3*time.Second)&nbsp; &nbsp; serve(serveChannel, quit)}至于您的最后一个问题:多个处理程序不会看到相同的请求。当一个处理程序从通道接收到一个值时,该值就会从通道中删除。下一个处理程序将接收下一个值。将通道视为可安全并发使用的先进先出队列。您可以在操场上找到代码的最后一次迭代。
随时随地看视频慕课网APP

相关分类

Go
我要回答