什么是goroutine,它们有多大?

期待Golang Developer课程的开始专业,我们邀请每个人参加免费的演示课程,主题为“ Go中的集成测试”



现在,我们传统上会发布有用的翻译。


, , - Go, , « » « ». , « 2 », , Go 1.4, , .

, , , , , Go.

Go 1.14, .

(Goroutine scheduler) (work-stealing) , Go 1.1 Go. . , , , , G, M P; , () .

«G» - Golang.

«M» - , - .

«P» ; , Go , .

: type g, type m type p.

, G (, ) M ( ) P ( ).

M , P P. Go , . , , G G - .

, main , g malg 

//  g  ,    stacksize .
func malg(stacksize int32) *g {
	newg := new(g)      // <---    
	if stacksize >= 0 {
		stacksize = round2(_StackSystem + stacksize)
		systemstack(func() {
			newg.stack = stackalloc(uint32(stacksize))
		})
		newg.stackguard0 = newg.stack.lo + _StackGuard
        newg.stackguard1 = ^uintptr(0)
        ...
        ...
    }
    return newg
}

newproc newproc1.

//   g,  fn  narg  ,   argp. callerpc -    go,   .  g    g,  .

func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) {
  ...
    acquirem() //     ,      p   var
	siz := narg
    siz = (siz + 7) &^ 7
  ...
	_p_ := _g_.m.p.ptr()
	newg := gfget(_p_)
	if newg == nil {
		newg = malg(_StackMin) // !!! <-   
		casgstatus(newg, _Gidle, _Gdead)
		allgadd(newg) 
	}
  ...
}      

, !

 

70 . :

type g struct {
    stack            stack   
    stackguard0      uintptr 
    stackguard1      uintptr 
    _panic           *_panic 
    _defer           *_defer 
    m                *m      
    sched            gobuf
    syscallsp        uintptr        
    syscallpc        uintptr        
    stktopsp         uintptr        
    param            unsafe.Pointer 
    atomicstatus     uint32
    stackLock        uint32 
    goid             int64
    schedlink        guintptr
    waitsince        int64      
    waitreason       waitReason
    preempt          bool 
    preemptStop      bool 
    preemptShrink    bool 
    asyncSafePoint   bool
    paniconfault     bool 
    gcscandone       bool 
    throwsplit       bool 
    activeStackChans bool
    raceignore       int8     
    sysblocktraced   bool     
    sysexitticks     int64   
    traceseq         uint64   
    tracelastp       puintptr 
    lockedm          muintptr
    sig              uint32
    writebuf         []byte
    sigcode0         uintptr
    sigcode1         uintptr
    sigpc            uintptr
    gopc             uintptr         
    ancestors        *[]ancestorInfo 
    startpc          uintptr         
    racectx          uintptr
    waiting          *sudog        
    cgoCtxt          []uintptr     
    labels           unsafe.Pointer
    timer            *timer        
    selectDone       uint32        
    gcAssistBytes    int64
}

 

!

; uintptr 64 , .. 8 , int64. 1 , - .

, timer (~70 ), _panic (~40 ) _defer (~100 ), 600 .

, … «2 »?

g stack.

type g struct {
	//  .
	// stack    : [stack.lo, stack.hi).
	// stackguard0 -  ,      Go.
	// stackguard1 -  ,      C.
...
	stack       stack  // ,  runtime/cgo
	stackguard0 uintptr // ,  liblink
	stackguard1 uintptr // ,  liblink

 

, , .

type stack struct {
	lo uintptr
	hi uintptr
}

, , : « ?», , 2 !

2 , - .

, . , Go , , , ; , runtime.morestack, , . , , , .

2048 , Go ; 1 64- 250 32- .

, runtime.abort. ; , ,

package main

func foo(i int) int {
	if i < 1e8 {
		return foo(i + 1)
	}
	return -1
}

func main() {
	foo(0)
}

 

, , runtime.abort .

$ go run exceed-stack.go
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x1071ce1, 0xe)
	/usr/local/go/src/runtime/panic.go:774 +0x72
runtime.newstack()
	/usr/local/go/src/runtime/stack.go:1046 +0x6e9
runtime.morestack()
	/usr/local/go/src/runtime/asm_amd64.s:449 +0x8f

goroutine 1 [running]:
main.foo(0xffffdf, 0x0)
...
...

, ?

, .

50 .

: ( ) .

$ ~ go run poc-goroutines-sizing.go

# 10  
Number of goroutines: 100000
Per goroutine:
  Memory: 2115.71 bytes
  Time:   1.404500 µs

# 1  
Number of goroutines: 1000000
Per goroutine:
  Memory: 2655.21 bytes
  Time:   1.518857 µs

# 3  
Number of goroutines: 3000000
Per goroutine:
  Memory: 2700.37 bytes
  Time:   1.637003 µs

# 6  
Number of goroutines: 6000000
Per goroutine:
  Memory: 2700.29 bytes
  Time:   2.541744 µs

# 9  
Number of goroutines: 9000000
Per goroutine:
  Memory: 2700.27 bytes
  Time:   2.857699 µs

# 12  
Number of goroutines: 12000000
Per goroutine:
  Memory: 2694.09 bytes
  Time:   3.232870 µs

# 50  
Number of goroutines: 50000000
Per goroutine:
  Memory: 2695.37 bytes
  Time:   5.098005 µs

!

, Go . , Go, , , .

src/runtime/HACKING.md, Golang .

, - - , Go.

!

  • https://stackoverflow.com/questions/8509152/max-number-of-goroutines

  • https://medium.com/a-journey-with-go/go-how-does-the-goroutine-stack-size-evolve-447fc02085e5

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

  • https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part1.html

  • https://medium.com/@genchilu/if-a-goroutine-call-a-new-goroutine-which-one-would-scheduler-pick-up-first-890002dc54f8

  • https://povilasv.me/go-scheduler/

package main

import (
	"flag"
	"fmt"
	"os"
	"runtime"
	"time"
)

var n = flag.Int("n", 3*1e6, "Number of goroutines to create")

var ch = make(chan byte)
var counter = 0

func f() {
	counter++
	<-ch//   
}

func main() {
	flag.Parse()
	if *n <= 0 {
		fmt.Fprintf(os.Stderr, "invalid number of goroutines")
		os.Exit(1)
	}

	//       1
	runtime.GOMAXPROCS(1)

	//   MemStats
	var m0 runtime.MemStats
	runtime.ReadMemStats(&m0)

	t0 := time.Now().UnixNano()
	for i := 0; i < *n; i++ {
		go f()
	}
	runtime.Gosched()
	t1 := time.Now().UnixNano()
	runtime.GC()

	//   MemStats
	var m1 runtime.MemStats
	runtime.ReadMemStats(&m1)

	if counter != *n {
		fmt.Fprintf(os.Stderr, "failed to begin execution of all goroutines")
		os.Exit(1)
	}

	fmt.Printf("Number of goroutines: %d\n", *n)
	fmt.Printf("Per goroutine:\n")
	fmt.Printf("  Memory: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(*n))
	fmt.Printf("  Time:   %f µs\n", float64(t1-t0)/float64(*n)/1e3)
}

-





All Articles