刚刚学习了golang GMP模型,现在我了解了goroutines、操作系统线程和golang上下文/处理器如何相互协作。但我还是不明白什么时候会产生M和P?
例如,我有一个测试代码来在数据库上运行一些操作,并且有两个测试用例(两批 goroutine):
func Test_GMP(t *testing.T) {
for _ = range []struct {
name string
}{
{"first batch"},
{"second batch"},
} {
goroutineSize := 50
done := make(chan error, goroutineSize)
for i := 0; i < goroutineSize; i++ {
go func() {
// do some databases operations...
// each goroutine should be blocked here for some time...
// propogate the result
done <- nil
}()
}
for i := 0; i < goroutineSize; i++ {
select {
case err := <-done:
assert.NoError(t, err)
case <-time.After(10 * time.Second):
t.Fatal("timeout waiting for txFunc goroutine")
}
}
close(done)
}
}
以我的理解,如果M是在需要时创建的。在第一批 goroutine 中,将创建 8 个(我计算机上的虚拟核心数量)操作系统线程,第二批将仅重用这 8 个操作系统线程,而不创建新线程。那是对的吗?
如果您可以提供有关此主题的更多材料或博客,我们将不胜感激。
仅当您的进程没有阻塞或没有任何系统调用时,M 才可重用。在你的情况下,你的内部有阻塞任务go func()
。因此,M 的数量不会限制为 8(我计算机上的虚拟核心数)。第一批将阻塞并从 P 中删除,并等待阻塞进程完成,同时新的 M 创建与 P 的关联。
-
我们通过Go func()创建一个goroutine;
-
存储G的队列有两个,一个是本地调度器P的本地队列,一个是全局G队列。新创建的G将是
保存在P的本地队列中,如果P的本地队列为
full,将保存在全局队列中;
-
G只能在m中运行,一个m必须持有一个P,M和P是1:1
关系。 M会从P的本地队列中弹出一个可执行文件G。
如果本地队列为空,你会认为其他MP组合
窃取可执行文件 G 来执行;
-
M调度G执行的一个进程是循环机制;
-
当M执行syscall或者剩下的阻塞操作时,M会阻塞,如果有一些g在执行,Runtime会移除这个
来自 P 的线程 M,然后创建一个新的操作系统线程(如果
有一个空闲线程可用于复用空闲线程)来服务
这个P;
-
当M系统调用结束时,该G将尝试获取一个空闲的P执行并将其放入该P的本地队列中。如果你得到P,那么这个
线程m变为睡眠状态,将其添加到空闲线程中,然后
这个G将会被放入全局队列中。
1、P数量:
环境变量$GomaxProcs是由Runtime决定的
方法gomaxprocs()时环境变量被调度。后
GO1.5、GomaxProcs 将默认设置为可用内核,并且
在默认之前它是 1。这意味着只有 $GOMAXPROCS Goroutine 是
任何时候同时运行执行。
2、M数量:
GO语言本身限制:当GO程序启动时,最大
number of M 将设置最大M数。然而,内核是
很难支持这么多线程,所以这个限制可以忽略。
SetMaxThreads函数在运行时/调试时,设置最大线程数M
一个M阻塞,你就会创建新的M。
M和P的数量没有绝对关系,一个m块,p
会创建或切换另一个M,所以即使P的默认数量是
1、可能有很多M出来。
请参阅以下内容了解更多详情,
- https://www.programmerought.com/article/79557885527/
- go-goroutine-os-thread-and-cpu-管理
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)