go 中线程数量的定义

2020-04-30 13:53:08 +08:00
 xmge

通过 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 是什么呢

3359 次点击
所在节点    程序员
11 条回复
mainjzb
2020-04-30 14:56:25 +08:00
runtime.GOMAXPROCS(1) 这行应该放在 init()函数的开头调用。我的测试是 6 线程,估计是 http 创建了 5 个线程,另外 1 个是 goroutin
xmge
2020-04-30 15:26:42 +08:00
@mainjzb 我这边换了位置后还是 7 个。与 runtime.GOMAXPROCS(1) 的位置好像关系不大。
zzzzzzkd
2020-04-30 17:10:53 +08:00
runtime.GOMAXPROCS(1)设置的是 P 的数量
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
2020-04-30 17:37:19 +08:00
@asAnotherJack 在代码测试 debug.SetMaxThreads 的确超过后就有错误 ; runtime: program exceeds 3-thread limit
fatal error: thread exhaustion 。 感觉大佬解释很靠谱
xmge
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
2020-04-30 18:30:22 +08:00
盲猜 cpu 虚拟核心数
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
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
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
2020-05-01 07:48:45 +08:00
超线程技术

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/667597

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX