正如我们所知,当goroutine必须执行阻塞调用(例如系统调用)或通过cgo调用C库时,可能会创建一个线程。一些测试代码:
package main
import (
"io/ioutil"
"os"
"runtime"
"strconv"
)
func main() {
runtime.GOMAXPROCS(2)
data, err := ioutil.ReadFile("./55555.log")
if err != nil {
println(err)
return
}
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 {}
}
当我运行它时,它没有创建很多线程。
➜ =99=[root /root]$ cat /proc/9616/status | grep -i thread
Threads: 5
有什么想法吗?
答案 0 :(得分:6)
我稍微修改了你的程序以输出更大的块
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 {}
}
然后按预期显示&gt; 200个线程
$ cat /proc/17033/status | grep -i thread
Threads: 203
所以我认为系统调用在原始测试中退出太快,以显示您期望的效果。
答案 1 :(得分:4)
goroutine是轻量级线程,它不等同于操作系统线程。 Language specification将其指定为同一地址空间内的&#34;独立并发控制线程&#34; 。
引用包runtime
的文档:
GOMAXPROCS变量限制了可以同时执行用户级Go代码的操作系统线程数。代表Go代码在系统调用中可以阻塞的线程数没有限制;那些不计入GOMAXPROCS限制。
仅仅因为你启动200个goroutines,它并不意味着将为它们启动200个线程。您将GOMAXPROCS
设置为2,这意味着可以有2&#34;活动&#34; goroutines同时运行。如果goroutine被阻止(例如I / O等待),则可能会产生新的线程。你没有提到你的测试文件有多大,你开始的goroutines可能会写得太快。
Effective Go博客文章将其定义为:
他们称之为 goroutines ,因为现有的术语 - 线程,协同程序,进程等 - 传达了不准确的内涵。 goroutine有一个简单的模型:它是一个与同一地址空间中的其他goroutine同时执行的函数。它是轻量级的,比堆栈空间的分配花费更多。并且堆栈开始很小,所以它们很便宜,并且可以根据需要分配(和释放)堆存储来增长。
Goroutines被多路复用到多个OS线程上,因此如果应该阻塞,例如在等待I / O时,其他线程继续运行。他们的设计掩盖了线程创建和管理的许多复杂性。
答案 2 :(得分:1)
issue 4056讨论了如何限制创建的实际线程数(而不是goroutine)。
Go 1.2在commit 665feee中介绍了线程限制管理。
您可以在pkg/runtime/crash_test.go#L128-L134
中看到一个测试,以检查是否实际创建了创建的线程数:
func TestThreadExhaustion(t *testing.T) {
output := executeTest(t, threadExhaustionSource, nil)
want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
if !strings.HasPrefix(output, want) {
t.Fatalf("output does not start with %q:\n%s", want, output)
}
}
该文件有一个示例,使用runtime.LockOSThread()
创建一个实际线程(对于给定的goroutine):
func testInNewThread(name string) {
c := make(chan bool)
go func() {
runtime.LockOSThread()
test(name)
c <- true
}()
<-c
}