[英]Why does it not create many threads when many goroutines are blocked in writing file in golang?
正如我们在go中所知,当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
有任何想法吗?
我稍微修改了你的程序以输出更大的块
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 {}
}
然后按预期显示> 200个线程
$ cat /proc/17033/status | grep -i thread
Threads: 203
所以我认为系统调用在原始测试中退出太快,以显示您期望的效果。
goroutine是一个轻量级线程,它不等同于操作系统线程。 语言规范将其指定为“在同一地址空间内的独立并发控制线程” 。
引用包runtime
的文档:
GOMAXPROCS变量限制了可以同时执行用户级Go代码的操作系统线程数。 代表Go代码在系统调用中可以阻塞的线程数没有限制; 那些不计入GOMAXPROCS限制。
仅仅因为你启动200个goroutines,它并不意味着将为它们启动200个线程。 您将GOMAXPROCS
设置为2,这意味着可以同时运行2个“活动”goroutine。 如果goroutine被阻止(例如I / O等待),则可能会产生新线程。 你没有提到你的测试文件有多大,你开始的goroutines可能会写得太快。
Effective Go博客文章将它们定义为:
它们被称为goroutines,因为现有的术语 - 线程,协同程序,进程等 - 传达了不准确的内涵。 goroutine有一个简单的模型:它是一个与同一地址空间中的其他goroutine同时执行的函数。 它是轻量级的,比堆栈空间的分配花费更多。 并且堆栈开始很小,因此它们很便宜,并且通过根据需要分配(和释放)堆存储来增长。
Goroutines被多路复用到多个OS线程上,因此如果应该阻塞,例如在等待I / O时,其他线程继续运行。 他们的设计隐藏了线程创建和管理的许多复杂性。
问题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
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.