本文介绍了Go程序的主要例程与产生的程序之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 gRPC 创建服务器时,如果我在主进程中启动 gRPC 服务器,它可以处理与来自客户的请求(数千)一样多。但是,如果我将服务器作为goroutine启动,它只能处理一些请求(数百个),并且在卡住后。我已经用一个非常简单的例子测试并确认了这一点,google.golang.org/grpc/examples/helloworld。



是因为衍生的goroutines堆栈大小非常小2K字节),而主要的goroutine更大?主要goroutine和产卵goroutines之间有什么区别?

示例。



greeter_server / main.go

 lis,err:= net.Listen(tcp,port)
if err!= nil {
log.Fatalf(未能听:%v,err)
}
s:= grpc.NewServer()
pb.RegisterGreeterServer(s,& server {})
s.Serve(lis)
}()

for {
}
}

greeter_client / main.go

  func main(){
//建立到服务器的连接。
for i:= 0;我< 500; i ++ {
conn,err:= grpc.Dial(address,grpc.WithInsecure())
if err!= nil {
log.Fatalf(did not connect:%v, err)
}
推迟conn.Close()
c:= pb.NewGreeterClient(conn)

for i:= 0;我< 500; i ++ {
//联系服务器并打印出响应。
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)
}
}
}


解决方案

为什么Goroutine的堆栈是无限的:

ref:




空循环:对于{
}

  p> 

使用100%的CPU核心,根据您可能使用的用例等待某些操作:

- sync .WaitGrou p

- 选择{}

- 频道

- time.Sleep






不,您可以尝试这两个示例来查看goroutines的堆栈限制是相同的:



在:

 包主

导入(
fmt
sync


var wg sync.WaitGroup

func main(){
wg.Add(1)
运行()
wg.Wait()
}
func run(){
s:=& S {a:1,b:2}
fmt.Println(s)
wg.Done()
}

type S struct {
a,b int
}

// String实现fmt.Stringer接口
func(s * S)String()string {
return fmt.Sprintf(%s,s)// Sprintf将调用s.String()
}


两个输出在Go Playground上都是一样的:

 运行时:goroutine堆栈超过250_000_000字节限制
致命错误:堆栈溢出

输出在具有 8 GB RAM的PC上:

 运行时:goroutine堆栈超过1_000_000_000字节限制
致命错误:堆栈溢出


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)
        }
    }
}
解决方案

Why is a Goroutine’s stack infinite:

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


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

这篇关于Go程序的主要例程与产生的程序之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-21 02:09