我有下一个代码:
par.go
package main import ( "runtime"; "time" ) func main() { runtime.GOMAXPROCS(4) ch := make(chan int) n := 1 for i := 0; i < n; i++ { go func() { time.Sleep(60 * time.Second) ch <- 1 }(); } for i := 0; i < n; i++ { <-ch } }
我用next运行它:
$ go build par.go $ time ./par
然后,确认此过程中有多少个线程:
$ ps -ef | grep par shubunt+ 3670 32131 0 12:35 pts/0 00:00:00 ./par $ cat /proc/3670/status | grep -i threads Threads: 5
您可以看到有5个线程。
如果我更改nin代码的值,那么接下来的情况是:
n
n := 100, Threads is 8 n := 10000, Threads is 9 n := 100000, Threads is 9 n := 1000000, Threads is 9 n := 2000000, Threads is 10
我知道,去调度的后续MPG机型,在这里P = 4,所以M = 4,M是1:1有KSE(内核线程)。如果任何goroutine处于任何阻塞状态,则它们P将从current脱离M,并找到一个空闲的M或新的Mif(如果找不到)。
MPG
P = 4
M = 4
M
1:1
KSE
P
所以,我的问题是:time.Sleep真的在阻止goroutine吗?如果不是,当我增加nfrom1到to的值时,为什么会有新线程出现2000000?如果是的话,那儿60seconds有,为什么只是调度程序又新又新M,我希望在那里有很多新线程?
time.Sleep
1
2000000
60seconds
test.go:
package main import ( "io/ioutil" "os" "runtime" "strconv" ) func main() { runtime.GOMAXPROCS(2) data := make([]byte, 128*1024*1024) for i := 0; i < 200; i++ { go func(n int) { for { err := ioutil.WriteFile("testxxx"+strconv.Itoa(n), []byte(data), os.ModePerm) if err != nil { println(err) break } } }(i) } select {} }
如果不使用Sleep,请使用真实的IO,线程号将202在我的计算机上。
Sleep
202
因此,我的问题还与上述两个示例的区别有关,何时我应该担心调度程序会为我生成太多内核线程?
[时间]。睡眠真的阻塞了goroutine吗?
是。
但是goroutine实际上如何安排给线程的方法是:a)复杂,b)每个版本中都不同,c)架构之间可能有所不同,并且d)语言未指定。如果“ MPG模型”是调度程序如何工作的准确模型,则它只是一个模型。
如果调度程序确定10个线程足以在它们全部运行时 不 运行200‘000个goroutine,time.Sleep则10个线程就足够了。
基本上,无需担心或思考Go中的此类内容(与其他语言(其中必须特别注意此类特殊性)形成鲜明对比)。“阻塞”仅表示下一条语句无法立即执行,因为实际语句尚未完成。如果原因不尽相同,可能会发生很多。睡眠除了等待,等待RAM,等待磁盘或等待网络数据外什么都不做。以相同的方式处理所有内容将简化调度程序,但会使调度程序变差。所以不,时间。睡眠不会阻塞goroutine。问题是“ block goroutine”不是具有定义含义的东西。它不需要定义,因为对此没有什么有趣的了解。
更新:
[W]我何时应该担心调度程序会为我生成太多内核线程?
决不。
有两种不同的情况:A)编写普通的,合理的生产代码; B)编写经过精心设计的手工代码,以创建许多线程,这些线程都在磁盘IO中等待完成。当然,您可以故意欺骗调度程序和OS,并提出一个病理程序,该程序会创建过多的线程( 可以 欺骗调度程序的问题是问题#4056中的地址),但这并不是要担心的事情。只是不要故意做愚蠢的事情。
有很多方法可以欺骗您的计算机。编写简洁代码是一种方法。种族探测器有助于识别它们。在编写竞赛条件之前担心竞赛条件是一件好事(因为这种情况会发生)。可能会创建过多的线程,您可以向操作系统询问线程数。并且在万一出现的情况太多的情况下:修复它。但这 不太可能 。有点像OOM错误:编写OOM的代码非常简单,但是在编写代码时不必担心OOM。如果您体验过OOM,则可以重新设计,但是不必担心OOM以及您需要了解有关OOM,如何防止它以及如何执行它而开始任何琐碎的项目。除非您已经知道您的数据使用了 大量 内存。同样在这里:如果您知道您的代码会做 大量 并发 磁盘IO,并且这是域 固有的 ,那么您可能会对此感到担忧并在代码中进行处理,但是每种语言的技术都是相同的。