Effective Go 中的客户端请求处理程序示例导致死锁?
Client request handler example from Effective Go leads to deadlock?
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
通道只针对一个请求,所以一旦一个请求进来,所有的 goroutines 都可以访问同一个请求。为什么这有用?
调用 serve
的代码不应该 运行 与填充通道的代码在同一个 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 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)
}
关于你的最后一个问题:多个处理程序不会看到相同的请求。一旦一个处理程序从通道接收到一个值,该值就会从中删除。下一个处理程序将接收下一个值。将通道视为可以安全并发使用的 First-In-First-Out 队列。
您可以在 the playground 上找到代码的最后一次迭代。
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
通道只针对一个请求,所以一旦一个请求进来,所有的 goroutines 都可以访问同一个请求。为什么这有用?
调用 serve
的代码不应该 运行 与填充通道的代码在同一个 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 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)
}
关于你的最后一个问题:多个处理程序不会看到相同的请求。一旦一个处理程序从通道接收到一个值,该值就会从中删除。下一个处理程序将接收下一个值。将通道视为可以安全并发使用的 First-In-First-Out 队列。
您可以在 the playground 上找到代码的最后一次迭代。