繁体   English   中英

并发http请求没有响应

[英]concurrent http request gives no response

我正在玩Go,我遇到了一个我无法解决的问题。

以下代码是重现我的问题的最不可能的代码。 原始代码的目标是将http请求委托给goroutines。 每个goroutine做了一些沉重的图像计算,并应该响应。

package main

import (
    "fmt"
    "runtime"
    "net/http"
)

func main() {
    http.HandleFunc("/", handle)
    http.ListenAndServe(":8080", nil)
}

func handle(w http.ResponseWriter, r *http.Request) {

    // the idea is to be able to handle several requests
    // in parallel

    // the "go" is problematic
    go delegate(w)
}

func delegate(w http.ResponseWriter) {

    // do some heavy calculations first

    // present the result (in the original code, the image)
    fmt.Fprint(w, "hello")
}

在的情况下, go delegate(w)我没有得到任何回应,没有go它的作品了很好的。

谁能解释一下发生了什么? 非常感谢!

ListenAndServe已经启动了goroutine来调用你的处理函数,所以你不应该自己动手。

这是包源中相关函数的代码

1089    func ListenAndServe(addr string, handler Handler) error {
1090        server := &Server{Addr: addr, Handler: handler}
1091        return server.ListenAndServe()
1092    }


1010    func (srv *Server) ListenAndServe() error {
1011        addr := srv.Addr
1012        if addr == "" {
1013            addr = ":http"
1014        }
1015        l, e := net.Listen("tcp", addr)
1016        if e != nil {
1017            return e
1018        }
1019        return srv.Serve(l)
1020    }


1025    func (srv *Server) Serve(l net.Listener) error {
1026        defer l.Close()
1027        var tempDelay time.Duration // how long to sleep on accept failure
1028        for {

1057            go c.serve()
1058        }
1059        panic("not reached")
1060    }


579 // Serve a new connection.
580 func (c *conn) serve() {
581     defer func() {
582         err := recover()

669         handler.ServeHTTP(w, w.req)

所以你的代码应该就是这样

func handle(w http.ResponseWriter, r *http.Request) {
    // the idea is to be able to handle several requests
    // in parallel
    // do some heavy calculations first

    // present the result (in the original code, the image)
    fmt.Fprint(w, "hello")
}

已经从“外部”goroutine(每个请求一个)调用处理程序。 处理程序必须完成所有必须完成的工作,例如在返回之前编写完整的响应。 你正在“过早地”返回多余的go语句的b / c。 请尝试简单地将“委托”的主体放在“句柄”中并检查是否有所改进;-)

Go调度程序有时候对Goroutines来说真的无情。 问题是这样的:你有一个应用程序,你运行例程,所以调度程序认为:嘿,我可能实际上做了一些优化。 为什么我不运行这个特定的Goroutine以节省一些CPU时间并使应用程序更具响应性?

这就是:在你的代码中没有办法强制Goroutine在某个时候完成。 事实上,Go文档说明了这一点:

例如,在此程序中:

  var a string func hello() { go func() { a = "hello" }() print(a) } 

对a的赋值没有跟随任何同步事件,因此不保证任何其他goroutine都能观察到它。 实际上,一个积极的编译器可能会删除整个go语句。

如果必须由另一个goroutine观察到goroutine的影响,请使用锁定或通道通信等同步机制来建立相对排序。

因此,您的问题的解决方案是添加同步事件,例如通过使用频道:

包主

import (
  "fmt"
  "net/http"
)

func main() {
    http.HandleFunc("/", handle)
    http.ListenAndServe(":8080", nil)
}

func handle(w http.ResponseWriter, r *http.Request) {
    // the idea is to be able to handle several requests
    // in parallel

    // the "go" is problematic...
    ch := make(chan int)
    go delegate(w, ch)
    // ...but not anymore:
    <-ch
}

func delegate(w http.ResponseWriter, ch chan<- int) {
    // do some heavy calculations first
    // present the result (in the original code, the image)
    fmt.Fprint(w, "hello")
    ch <- 1
}

来自: Go Memory Model

无论如何,正如其他人所指出的,你的例子目前是人为的。 但是有一些情况下,从http处理程序中调用其他Goroutines是有意义的。 例如,如果您同时进行繁重的计算和HTTP流式传输,或同时进行多次繁重的计算。 虽然我认为在后一种情况下你可能会自己添加一个频道用于同步目的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM