V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xmge
V2EX  ›  程序员

go 中线程数量的定义

  •  
  •   xmge · 2020-04-30 13:53:08 +08:00 · 3344 次点击
    这是一个创建于 1672 天前的主题,其中的信息可能已经有所发展或是发生改变。

    通过 pprof 查看 go 程序的运行状况时,发现 threadcreate 总是 7 或者 8 为什么呢?

    本机 runtime.NumCPU() = 4

    code:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	_ "net/http/pprof"
    	"runtime"
    )
    var c = make(chan int)
    
    
    func init() {
    	for i:=0;i<10;i++{
    		go func(i int) {
    			fmt.Println("push : ",i)
    			c <- i
    		}(i)
    	}
    }
    
    func main() {
    	// 设置使用 cpu 的最大核数
    	runtime.GOMAXPROCS(1)
    	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    		for i:=0;i<5;i++{
    			fmt.Println("pop : ",<-c)
    		}
    		writer.Write([]byte("hello world"))
    	})
    	log.Fatal( http.ListenAndServe(":8000",nil))
    }
    

    然后访问 http://localhost:8000/debug/pprof/threadcreate?debug=1,结果为:

    threadcreate profile: total 7
    6 @
    #	0x0
    
    1 @ 0x103ae7e 0x103b589 0x1037d44 0x1037d45 0x1067401
    #	0x103ae7d	runtime.allocm+0x14d			/usr/local/go/go1.14.2/src/runtime/proc.go:1390
    #	0x103b588	runtime.newm+0x38			/usr/local/go/go1.14.2/src/runtime/proc.go:1704
    #	0x1037d43	runtime.startTemplateThread+0x2c3	/usr/local/go/go1.14.2/src/runtime/proc.go:1768
    #	0x1037d44	runtime.main+0x2c4			/usr/local/go/go1.14.2/src/runtime/proc.go:186
    
    

    哪位大佬给解释下,为什么是 7 个线程。 其中 6 @ 0x0 是什么呢

    第 1 条附言  ·  2020-04-30 17:32:52 +08:00
    demo 中 runtime.GOMAXPROCS(1) 与本文的问题没有很大关系。只是设置了使用 cpu 核数为 1.
    11 条回复    2020-05-01 07:48:45 +08:00
    mainjzb
        1
    mainjzb  
       2020-04-30 14:56:25 +08:00
    runtime.GOMAXPROCS(1) 这行应该放在 init()函数的开头调用。我的测试是 6 线程,估计是 http 创建了 5 个线程,另外 1 个是 goroutin
    xmge
        2
    xmge  
    OP
       2020-04-30 15:26:42 +08:00
    @mainjzb 我这边换了位置后还是 7 个。与 runtime.GOMAXPROCS(1) 的位置好像关系不大。
    zzzzzzkd
        3
    zzzzzzkd  
       2020-04-30 17:10:53 +08:00 via iPhone
    runtime.GOMAXPROCS(1)设置的是 P 的数量
    asAnotherJack
        4
    asAnotherJack  
       2020-04-30 17:25:58 +08:00
    根据 GMP 模型,GOMAXPROCS 设置的是 P 的数量,限制同时运行的线程数,不是限制的 M 的数量,M 的最大数量可以看 src/runtime/runtime2.go 结构体 schedt 有个字段 maxmcount 注释 // maximum number of m's allowed (or die),默认是 10000,改的话可以通过 debug.SetMaxThreads,超过数量直接 crash 。
    可以通俗理解一下,比如限制并发数为 1,如果只允许创建一个线程的话,当这个线程阻塞了,又不允许创建新线程,那并发就变 0 了,不合适的
    不知道理解的对不对,等大佬解惑
    xmge
        5
    xmge  
    OP
       2020-04-30 17:37:19 +08:00
    @asAnotherJack 在代码测试 debug.SetMaxThreads 的确超过后就有错误 ; runtime: program exceeds 3-thread limit
    fatal error: thread exhaustion 。 感觉大佬解释很靠谱
    xmge
        6
    xmge  
    OP
       2020-04-30 17:50:20 +08:00
    ```
    func schedinit() {
    // raceinit must be the first call to race detector.
    // In particular, it must be done before mallocinit below calls racemapshadow.
    _g_ := getg()
    if raceenabled {
    _g_.racectx, raceprocctx0 = raceinit()
    }

    sched.maxmcount = 10000

    tracebackinit()
    moduledataverify()
    stackinit()
    mallocinit()
    fastrandinit() // must run before mcommoninit
    mcommoninit(_g_.m)
    ..

    }

    ```
    wysnylc
        7
    wysnylc  
       2020-04-30 18:30:22 +08:00
    盲猜 cpu 虚拟核心数
    mainjzb
        8
    mainjzb  
       2020-04-30 18:41:26 +08:00
    ```
    package main

    import (
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof"
    "runtime"
    )
    var c = make(chan int)


    func init() {
    runtime.GOMAXPROCS(1) // 改成 50,线程数可以显著提升
    for i:=0;i<100000;i++{
    go func(i int) {
    fmt.Println("push : ",i)
    c <- i
    }(i)
    }
    }

    func main() {
    http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    writer.Write([]byte("hello world"))
    })
    log.Fatal( http.ListenAndServe(":8000",nil))
    }
    ```
    kaifang
        9
    kaifang  
       2020-04-30 18:47:14 +08:00
    GPM 模型
    G:Goruntine,待执行的 Goruntine 任务队列,分全局和本地
    P: 本地调度器,运行在线程 M 上的本地调度器,持有 G
    M: 线程

    runtime.GOMAXPROCS(1) 这个是逻辑上的核心数并不是物理上的核心数,也就是规定同时只有一个 P 会在线程 M 上去工作,至于线程 M 肯定是要有多个的,因为 G 阻塞之后 P 会找下一个 M 再进行调度运行 G 。核心数不是越多越好,一般默认是物理核心数。

    可以参考:

    https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-goroutine/
    https://www.bilibili.com/video/BV1g4411R7p5
    mainjzb
        10
    mainjzb  
       2020-04-30 19:05:37 +08:00
    The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the limit.
    tairan2006
        11
    tairan2006  
       2020-05-01 07:48:45 +08:00 via Android
    超线程技术
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5512 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 08:58 · PVG 16:58 · LAX 00:58 · JFK 03:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.