为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕无忌1623718 2022-06-27 10:44:36
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 都可以访问同一个请求。为什么这很有用?
查看完整描述

1 回答

?
三国纷争

TA贡献1804条经验 获得超7个赞

调用的代码serve不应该与填满通道的代码在同一个 goroutine 中运行。


在您的代码中,serve启动处理程序 goroutines 但随后等待<-quit. 由于它被阻止,您永远无法访问填充的代码serveChannel。所以工人们从来没有任何东西可以消费。你也永远不会通知quit,让serve等待永远。


第一步是serveChannel在单独的 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 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)

}

至于您的最后一个问题:多个处理程序不会看到相同的请求。当一个处理程序从通道接收到一个值时,该值就会从通道中删除。下一个处理程序将接收下一个值。将通道视为可安全并发使用的先进先出队列。


您可以在操场上找到代码的最后一次迭代。


查看完整回答
反对 回复 2022-06-27
  • 1 回答
  • 0 关注
  • 112 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信