一尘不染

time.Sleep会阻止goroutine吗?

go

我有下一个代码:

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 := 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 = 4M1:1KSE(内核线程)。如果任何goroutine处于任何阻塞状态,则它们P将从current脱离M,并找到一个空闲的M或新的Mif(如果找不到)。

所以,我的问题是:time.Sleep真的在阻止goroutine吗?如果不是,当我增加nfrom1到to的值时,为什么会有新线程出现2000000?如果是的话,那儿60seconds有,为什么只是调度程序又新又新M,我希望在那里有很多新线程?

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在我的计算机上。

因此,我的问题还与上述两个示例的区别有关,何时我应该担心调度程序会为我生成太多内核线程?


阅读 368

收藏
2020-07-02

共1个答案

一尘不染

[时间]。睡眠真的阻塞了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,并且这是域 固有的
,那么您可能会对此感到担忧并在代码中进行处理,但是每种语言的技术都是相同的。

2020-07-02