为什么 Goroutine 在这个 HTTP 服务器中阻塞了主函数?

huangapple go评论81阅读模式
英文:

why goroutine block main func in this http server?

问题

我想要使用httprouter在两个端口(8888和8080)上设置一个HTTP服务器,就像下面的代码一样。

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "log"
    "net/http"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\n")
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)

    fmt.Println("监听8080端口")
    // 这里会阻塞
    go log.Fatal(http.ListenAndServe(":8080", router))
    fmt.Println("监听8888端口")
    log.Fatal(http.ListenAndServe(":8888", router))
}

但是它不能正常工作,我的服务器只监听8080端口。如果我做一些更改:

go func() { log.Fatal(http.ListenAndServe(":8080", router)) }()

它可以在80808888上正常工作。那么为什么会这样呢?是因为闭包还是其他原因?

英文:

I want to setup a http server with httprouter listening on two ports 8888 and 8080 just like the code below.

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "log"
    "net/http"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\n")
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)

    fmt.Println("listen on 8080")
    // this is where blocked
    go log.Fatal(http.ListenAndServe(":8080", router))
    fmt.Println("listen on 8888")
    log.Fatal(http.ListenAndServe(":8888", router))
}

But it doesn't work properly,my server only listen on 8080.If I make some change:

go func() { log.Fatal(http.ListenAndServe(":8080", router)) }()

It works finely both on 8080 and 8888.So why? It's about closure or something else?

答案1

得分: 9

“函数值和参数在调用的goroutine中按照通常的方式进行评估。” —— Go语言规范,“Go语句”

你正在为调用log.Fatal创建一个goroutine,但是log.Fatal参数在主goroutine中先进行评估。而Fatal的参数是http.ListenAndServe的返回值。因此,在ListenAndServe返回之后,新的goroutine才会开始执行。

英文:

> The function value and parameters are evaluated as usual in the calling goroutine

Go language spec, "Go statements".

You're creating a goroutine for the call to log.Fatal, but the arguments to log.Fatal are evaluated beforehand, in the main goroutine. And Fatal's argument is the return value of http.ListenAndServe. So the new goroutine doesn't start until after ListenAndServe returns.

答案2

得分: 8

由于http.ListenAndServe()是阻塞的,并且在您的场景中有两个实例,所以尝试将其中一个放在一个goroutine中。这个想法是将这两个Web服务器初始化语句的执行分离到单独的goroutine中。

func main() {
    router := httprouter.New()
    router.GET("/", Index)

    go func() {
        fmt.Println("监听8080端口")
        log.Fatal(http.ListenAndServe(":8080", router))
    }()

    fmt.Println("监听8888端口")
    log.Fatal(http.ListenAndServe(":8888", router))
}
英文:

Since http.ListenAndServe() is blocking, and in your scenario, there are two of them, then try to put one of them in a goroutine. The idea is to separate the execution of those two web server initialization statements into separate goroutine.

func main() {
	router := httprouter.New()
	router.GET("/", Index)

	go func() {
		fmt.Println("listen on 8080")
		log.Fatal(http.ListenAndServe(":8080", router))
	}()

	fmt.Println("listen on 8888")
	log.Fatal(http.ListenAndServe(":8888", router))
}

huangapple
  • 本文由 发表于 2017年5月9日 12:19:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/43861055.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定