当前位置: 首页 > 面试题库 >

time.Sleep会阻止goroutine吗?

淳于亦
2023-03-14
问题内容

我有下一个代码:

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

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


问题答案:

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



 类似资料:
  • 问题内容: 在GO教程中,我们有这张幻灯片:Goroutines 运行此代码会产生预期的结果(“ world”和“ hello”交替写入屏幕5次)。 但是,如果我们注释掉了(因此是导入行)并再次运行该程序, 则只剩下 “ hello”被写入屏幕五次了。 有什么重要的意义可以使goroutine免于死亡? 问题答案: 如果将其删除,则不会给goroutine运行的机会。goroutine调度程序不是

  • 问题内容: 作为一个愚蠢的基本线程练习,我一直在尝试在golang中实现睡眠理发师问题。使用通道,这应该很容易,但是我遇到了heisenbug。也就是说,当我尝试诊断时,问题消失了! 考虑以下。该函数将整数(或“客户”)压入通道。阅读频道来剪掉“顾客”的头发。如果我在函数中插入一条语句,程序将按预期运行。否则,切勿剪任何人的头发。 知道发生什么了吗? 问题答案: 问题是Go的调度程序的实现方式。当

  • 问题内容: 每个响应式网站开发教程都建议使用CSS属性来隐藏内容,以防止内容在移动浏览器中加载,从而使网站加载速度更快。是真的吗 难道不加载图像或它仍然加载在手机浏览器的内容?有什么方法可以防止在移动浏览器上加载不必要的内容? 问题答案: 浏览器变得越来越聪明。今天,如果浏览器(取决于版本)可以确定图像没有用,则可能会跳过图像加载。 该图像具有样式,但是脚本可以读取其大小。如果父级为隐藏状态,Ch

  • 问题内容: 我有一个goroutine,它调用一个方法,并在通道上传递返回的值: 如何停止这种goroutine? 问题答案: 编辑: 在意识到您的问题是关于将值发送到goroutine中的chan之前,我匆忙编写了此答案。 下面的方法可以与上面建议的其他chan一起使用,或者利用您已经拥有的chan双向的事实,您可以只使用一个… 如果您的goroutine仅用于处理来自chan的项目,则可以使用

  • 问题内容: 我对Go如何处理非阻塞IO感到困惑。API在我看来基本上是同步的,并且在Go上观看演示时,听到诸如“和调用块”之类的注释并不罕见。 从文件或网络读取时,Go是否使用阻塞IO?还是当在Go Routine中使用某种魔术来重写代码? 来自C#背景,这感觉非常不直观,在C#中,当使用异步API时我们使用了关键字。这清楚地表明,API可以产生当前线程,并在以后的延续中继续。 因此,TLDR;当

  • 问题内容: 我有以下代码片段。 如您所见,我在goroutine中运行了两个函数errName和errEmail。我将错误类型的通道作为参数传递。如果其中一个先完成,它应该通过通道发送错误并关闭它。因此,第二个仍在运行的goroutine不再需要运行,因为我已经遇到了错误,并且我想终止仍在运行的goroutine。这就是我在上面的示例中尝试达到的目标。 当我运行程序时,出现错误 我知道,当我删除c