猿问

golang写文件时阻塞了很多goroutine,为什么不创建很多线程?

正如我们在 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

有任何想法吗?


UYOU
浏览 453回答 3
3回答

慕标5832272

goroutine 是一个轻量级线程,它不等同于操作系统线程。的语言规范指定它作为一个“相同的地址空间内的控制的独立并发线程”。引用 package 的文档runtime:GOMAXPROCS 变量限制了可以同时执行用户级 Go 代码的操作系统线程的数量。代表Go代码在系统调用中可以阻塞的线程数没有限制;这些不计入 GOMAXPROCS 限制。仅仅因为您启动了 200 个 goroutine,并不意味着将为它们启动 200 个线程。您设置GOMAXPROCS为 2,这意味着可以同时运行 2 个“活动”goroutine。如果 goroutine 被阻塞(例如 I/O 等待),可能会产生新线程。你没有提到你的测试文件有多大,你开始的 goroutines 可能写得太快了。在有效围棋博客文章将它们定义为:它们被称为goroutines是因为现有的术语——线程、协程、进程等——传达了不准确的内涵。goroutine 有一个简单的模型:它是一个与同一地址空间中的其他 goroutine 并发执行的函数。它是轻量级的,成本比分配堆栈空间多一点。并且堆栈开始时很小,因此它们很便宜,并且通过根据需要分配(和释放)堆存储来增长。Goroutines 被多路复用到多个 OS 线程上,所以如果一个应该阻塞,例如在等待 I/O 时,其他人继续运行。它们的设计隐藏了线程创建和管理的许多复杂性。

ITMISS

该问题4056只讨论如何限制创建实际的线程(未够程)的数量。Go 1.2 在commit 665feee 中引入了线程限制管理。您可以看到一个测试,以检查是否实际达到了创建的线程数pkg/runtime/crash_test.go#L128-L134:func TestThreadExhaustion(t *testing.T) {&nbsp; &nbsp; output := executeTest(t, threadExhaustionSource, nil)&nbsp; &nbsp; want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"&nbsp; &nbsp; if !strings.HasPrefix(output, want) {&nbsp; &nbsp; &nbsp; &nbsp; t.Fatalf("output does not start with %q:\n%s", want, output)&nbsp; &nbsp; }}同一个文件有一个创建实际线程的示例(对于给定的 goroutine),使用runtime.LockOSThread():func testInNewThread(name string) {&nbsp; &nbsp; c := make(chan bool)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; runtime.LockOSThread()&nbsp; &nbsp; &nbsp; &nbsp; test(name)&nbsp; &nbsp; &nbsp; &nbsp; c <- true&nbsp; &nbsp; }()&nbsp; &nbsp; <-c}

胡子哥哥

我稍微修改了你的程序以输出一个更大的块package mainimport (&nbsp; &nbsp; "io/ioutil"&nbsp; &nbsp; "os"&nbsp; &nbsp; "runtime"&nbsp; &nbsp; "strconv")func main() {&nbsp; &nbsp; runtime.GOMAXPROCS(2)&nbsp; &nbsp; data := make([]byte, 128*1024*1024)&nbsp; &nbsp; for i := 0; i < 200; i++ {&nbsp; &nbsp; &nbsp; &nbsp; go func(n int) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err := ioutil.WriteFile("testxxx"+strconv.Itoa(n), []byte(data), os.ModePerm)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; println(err)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }(i)&nbsp; &nbsp; }&nbsp; &nbsp; select {}}然后,这会按您的预期显示 >200 个线程$ cat /proc/17033/status | grep -i threadThreads:&nbsp; &nbsp; 203所以我认为系统调用在您的原始测试中退出得太快,无法显示您期望的效果。
随时随地看视频慕课网APP

相关分类

Go
我要回答