我有下一个代码:
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个线程。
如果我更改n
in代码的值,那么接下来的情况是:
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 = 4
,M
是1:1
有KSE
(内核线程)。如果任何goroutine处于任何阻塞状态,则它们P
将从current脱离M
,并找到一个空闲的M
或新的M
if(如果找不到)。
所以,我的问题是:time.Sleep
真的在阻止goroutine吗?如果不是,当我增加n
from1
到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