登录
首页 >  Golang >  Go问答

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

来源:stackoverflow

时间:2024-04-30 20:09:36 471浏览 收藏

本篇文章给大家分享《effective Go 中的客户端请求处理程序示例会导致死锁吗?》,覆盖了Golang的常见基础知识,其实一个语言的全部知识点一篇文章是不可能说完的,但希望通过这些问题,让读者对自己的掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握它。

问题内容

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 错误:所有 goroutine 都在睡觉 - 死锁!

即使我从概念上不理解程序中的问题,我也不理解原始代码是如何工作的。我确实了解 maxoutstanding goroutine 是生成的,并且它们都监听单个 clientrequests 通道。但是 clientrequests 通道仅适用于一个请求,因此一旦请求进入,所有 goroutine 都可以访问同一请求。为什么这有用?


解决方案


调用 serve 的代码不应该与填充通道的 goroutine 运行在同一个 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 goroutine are sleeped 。这是因为 serve 最终等待 quit 信号,但没有任何内容可发送。

在正常程序中,在捕获信号或某些 shutdown 请求后,将填充 quit。由于我们没有任何东西,我们将在三秒后将其关闭,也是在一个单独的 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)
}

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

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

本篇关于《effective Go 中的客户端请求处理程序示例会导致死锁吗?》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于Golang的相关知识,请关注golang学习网公众号!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>