douhuxi4145 2016-09-14 14:57
浏览 113

Go程序的主要goroutine和衍生的goroutine之间的区别

When creating a server using gRPC, if I start the gRPC server in the main process, it can deal with as many as requests (thousands) from clients. However, if I start the server as a goroutine, it can only handle some requests (hundreds) and after get stuck. I have tested and confirmed this with a very simple example, google.golang.org/grpc/examples/helloworld.

Is it because spawned goroutines stack size is very small (2Kbytes), and the main goroutine's much larger? What's the difference between the main goroutine and spawned goroutines?

Example link. Modified parts of the example as follows.

greeter_server/main.go

func main() {
    go func() {
        lis, err := net.Listen("tcp", port)
        if err != nil {
            log.Fatalf("failed to listen: %v", err)
        }   
        s := grpc.NewServer()
        pb.RegisterGreeterServer(s, &server{})
        s.Serve(lis)
    }() 

    for {
    }   
}

greeter_client/main.go

func main() {
    // Set up a connection to the server.
    for i := 0; i < 500; i++ {
        conn, err := grpc.Dial(address, grpc.WithInsecure())
        if err != nil {
            log.Fatalf("did not connect: %v", err)
        }
        defer conn.Close()
        c := pb.NewGreeterClient(conn)

        for i := 0; i < 500; i++ {
            // Contact the server and print out its response.
            name := defaultName
            if len(os.Args) > 1 {
                name = os.Args[1]
            }
            r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
            if err != nil {
                log.Fatalf("could not greet: %v", err)
            }
            log.Printf("%d's Greeting: %s", i, r.Message)
        }
    }
}
  • 写回答

1条回答 默认 最新

  • dsfgds4215 2016-09-14 16:05
    关注

    Why is a Goroutine’s stack infinite:

    One of the key features of Goroutines is their cost; they are cheap to create in terms of initial memory footprint (as opposed to the 1 to 8 megabytes with a traditional POSIX thread) and their stack grows and shrinks as necessary. This allows a Goroutine to start with a single 4096 byte stack which grows and shrinks as needed without the risk of ever running out.

    There is however one detail I have withheld until now, which links the accidental use of a recursive function to a serious case of memory exhaustion for your operating system, and that is, when new stack pages are needed, they are allocated from the heap.

    As your infinite function continues to call itself, new stack pages are allocated from the heap, permitting the function to continue to call itself over and over again. Fairly quickly the size of the heap will exceed the amount of free physical memory in your machine, at which point swapping will soon make your machine unusable.

    The size of the heap available to Go programs depends on a lot of things, including the architecture of your CPU and your operating system, but it generally represents an amount of memory that exceeds the physical memory of your machine, so your machine is likely to swap heavily before your program ever exhausts its heap.

    ref: http://dave.cheney.net/2013/06/02/why-is-a-goroutines-stack-infinite


    Empty loop:

    for{
    }
    

    uses 100% of a CPU Core, to wait for some operation depending to the use case you may use:
    - sync.WaitGroup like this
    - select {} like this
    - channels
    - time.Sleep


    Is it because spawned goroutines stack size is very small (2Kbytes), and the main goroutine's much larger?

    No, you may try these two samples to see the stack limit of goroutines are the same:
    one main goroutine on The Go Playground,
    try second goroutine on The Go Playground:

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var wg sync.WaitGroup
    
    func main() {
        wg.Add(1)
        go run()
        wg.Wait()
    }
    func run() {
        s := &S{a: 1, b: 2}
        fmt.Println(s)
        wg.Done()
    }
    
    type S struct {
        a, b int
    }
    
    // String implements the fmt.Stringer interface
    func (s *S) String() string {
        return fmt.Sprintf("%s", s) // Sprintf will call s.String()
    }
    

    both outputs are the same on the Go Playground:

    runtime: goroutine stack exceeds 250_000_000-byte limit
    fatal error: stack overflow
    

    outputs on a PC with 8 GB RAM:

    runtime: goroutine stack exceeds 1_000_000_000-byte limit
    fatal error: stack overflow
    
    评论

报告相同问题?

悬赏问题

  • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
  • ¥15 解riccati方程组
  • ¥15 display:none;样式在嵌套结构中的已设置了display样式的元素上不起作用?
  • ¥30 用arduino开发esp32控制ps2手柄一直报错
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
  • ¥50 树莓派安卓APK系统签名
  • ¥65 汇编语言除法溢出问题
  • ¥15 Visual Studio问题
  • ¥20 求一个html代码,有偿