在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存。 Go 语言的调度器其实就是通过使用数量合适的线程并在每一个线程上执行更多的工作来降低操作系统和硬件的负载。 Goroutine 只存在于 Go 语言的运行时,它是 Go 语言在用户态为我们提供的『线程』,如果一个 Goroutine 由于 IO 操作而陷入阻塞,操作系统并不会对上下文进行切换,但是 Go 语言的调度器会将陷入阻塞 Goroutine 『切换』下去等待系统调用结束并让出计算资源,作为一种粒度更细的资源调度单元,如果使用得当能够在高并发的场景下更高效地利用机器的 CPU。 虽然 Goroutine 在运行时中定义的状态非常多而且复杂,但是我们可以将这些不同的状态聚合成最终的三种:等待中、可运行、运行中,在运行期间我们会在这三种不同的状态来回切换: Go 语言并发模型中的 M 其实表示的是操作系统线程,在默认情况下调度器能够允许创建 10000 个线程,但是其中绝大多数的线程都不会执行用户代码(可能陷入系统调用),最多只会有 GOMAXPROCS 个线程 M 能够正常运行。 所有 Golang 程序中的最大『可运行』线程数其实就等于 GOMAXPROCS 这个变量的值;在默认情况下,它会被设置成当前应用的核数,我们也可以使用 runtime.GOMAXPROCS 方法来改变当前程序中最大的线程数。 在大多数情况下,我们都会使用 Go 的默认设置,也就是 #thread == #CPU,在这种情况下不会触发操作系统级别的线程调度和上下文切换,所有的调度都会发生在用户态,由 Go 语言调度器触发,能够减少非常多的额外开销。 操作系统线程在 Go 语言中就会使用私有结构体 m 来表示, 其中 g0 是持有调度堆栈的 Goroutine,curg 是在当前线程上运行的 Goroutine,这也是作为操作系统线程唯一关心的两个 Goroutine 了。 Go 语言调度器中的最后一个重要结构就是处理器 P,其实就是线程需要的上下文环境,也是用于处理代码逻辑的处理器,通过处理器 P 的调度,每一个内核线程 M 都能够执行多个 G,这样就能在 G 进行一些 IO 操作时及时对它们进行切换,提高 CPU 的利用率。 每一个 Go 语言程序中所以处理器的数量一定会等于 GOMAXPROCS,这是因为调度器在启动时就会创建 GOMAXPROCS 个处理器 P,这些处理器会绑定到不同的线程 M 上并为它们调度 Goroutine。 处理器在 Go 语言运行时中同样使用私有结构体 p 表示, 我们将结构体中 GC 以及用于追踪调试的字段全部删除以简化这里需要展示的属性,在上述字段中,status 表示了当前处理器的状态,runhead、runqtail、runq 以及 runnext 等字段表示处理器持有的运行队列,运行队列中就包含待执行的 Goroutine 列表。 p 结构体中的状态 status 其实就会是以下五种状态其中的一种,我们能在 runtime2.go#L99-L147 文件中找到处理器 P 的全部状态: |
请发表评论