Kez*_*ong 4 multithreading operating-system schedule go goroutine
刚刚学习了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的本地队列已满,则会保存到全局队列中;
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程序启动时,M的最大数量会设置M的最大数量。但是内核很难支持这么多线程,所以这个限制可以忽略。SetMaxThreads函数在运行时/调试时,设置AM阻塞的最大M个数,否则会创建新的M。
M和P的数量没有绝对的关系,一个m块,p会创建或切换另一个M,所以即使P的默认数量是1,也可能有很多M出来。
请参阅以下内容了解更多详情,