Golang http server blocks when starts a goroutine

2019-01-20 12:38发布

问题:

As i learned from golang docs, if i set runtime.GOMAXPROCS(8) with a cpu of 8 cores (intel i7), then start a goroutine of infinite-loop, other gorutines should not be blocked because there are engough threads and goprocs. But this is not true when using net/http package, an infinite-loop goroutine will block http server after a few invocations. Can anyone help to explain why ?

  1. If i comment the line of "go infinite loop", start client after server, client will output 1000 asterisks; but if i enable the goroutine, client will block after print a few asterisks
  2. I have tried add runtime.LockOSThread() in the goroutine, it seems that doesn't work
  3. My Environment: osx 10.10, go version go1.3.1 darwin/amd64

Server code:

package main

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

func myHandler(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("hello"))
}

func infiniteloop() {
    for {

    }
}

func main() {
    // set max procs for multi-thread executing
    runtime.GOMAXPROCS(runtime.NumCPU())

    // print GOMAXPROCS=8 on my computer
    fmt.Println("GOMAXPROCS=", runtime.GOMAXPROCS(-1))
    http.Handle("/", http.HandlerFunc(myHandler))

    // uncomment below line cause server block after some requests 
    // go infiniteloop()
    if err := http.ListenAndServe(":8280", nil); err != nil {
        log.Fatal(err)
    }
}

Client code:

package main
import (
    "fmt"
    "net/http"
)

func getOnce() {
    if resp, err := http.Get("http://localhost:8280"); err != nil {
        fmt.Println(err)
        return
    } else {
        defer func() {
            if err := resp.Body.Close(); err != nil {
                fmt.Println(err)
            }
        }()
        if resp.StatusCode != 200 {
            fmt.Println("error codde:", resp.StatusCode)
            return
        } else {
            fmt.Print("*")

        }
    }
}

func main() {
    for i := 1; i < 1000; i++ {
        getOnce()
        if i%50 == 0 {
            fmt.Println()
        }
    }

}

Now i know why such emtpy loop block other goroutines, but why runtime.LockOSThread() doesn't help either?

func infiniteloop() {
    // add LockOSThread will not help
    runtime.LockOSThread()
    for {
    }
}

As http://golang.org/pkg/runtime/#LockOSThread mentioned, the empty loop should be executed in an standalone thread, and other goroutines should not be impacted by the busy loop. What's wrong in my understanding?

回答1:

The Go runtime's scheduler is not fully pre-emptive at this time. Go 1.2 improved matters by occasionally calling into the scheduler on function calls, but the infinite loops in your example have no function calls so this doesn't help.

With an actual body to your infinite loop handlers, you may see better behaviour. Alternatively, a manual call to runtime.Gosched may help in cases like this.



回答2:

The scheduler might not be able to preempt such an empty "infinite" loop. The scheduler got better and better during the last release, maybe he should be good enough for such code; he definitely is good enough for real code. Just don't do such nonsense.