簡體   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