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 unity第一人称射击小游戏,有demo,在原脚本的基础上进行修改以达到要求
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染