Goroutines 是 Go 设计的基石,为并发编程提供了强大的机制。 作为轻量级协程,它们简化了并行任务执行。 启动 goroutine 非常简单:只需在函数调用前添加 go
关键字即可启动异步执行。主程序继续执行,无需等待 goroutine 完成。
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
并发: 在单个 CPU 上看似同时管理多个任务的能力。 CPU 在任务之间快速切换,产生并行执行的错觉。 虽然微观上是顺序的,但宏观上却是并发的。
并行性:跨多个CPU真正同时执行多个任务,消除CPU资源争用。
进程:具有自己的资源(内存、文件等)的独立执行环境。 进程之间的切换是资源密集型的,需要内核级干预。
线程:进程内的轻量级执行单元,共享进程的资源。 线程切换比进程切换开销更少。
协程维护自己的寄存器上下文和堆栈。 协程之间的切换涉及保存和恢复此状态,从而允许它们从中断处恢复执行。 与进程和线程不同,协程管理是在用户程序中处理的,而不是在操作系统中处理的。 Goroutines 是一种特定类型的协程。
Go的高效并发依赖于GPM调度模型。 涉及四个关键组件:M、P、G 和 Sched(Sched 未在图中描绘)。
M(机器):内核级线程。 Goroutines 在 Ms.
上运行G(Goroutine): 单个 Goroutine。 每个 G 都有自己的堆栈、指令指针和其他与调度相关的信息(例如,它正在等待的通道)。
P(处理器): 管理和执行 goroutine 的逻辑处理器。它维护一个就绪 goroutine 的运行队列。
Sched(调度器):中央调度器,管理M和G队列并确保高效的资源分配。
该图显示了两个操作系统线程 (M),每个线程都有一个执行 goroutine 的处理器 (P)。
GOMAXPROCS()
控制 P 的数量(从而控制真正的并发级别)。
灰色 G 已准备就绪,但尚未运行。 P 管理这个运行队列。
启动一个 goroutine 将其添加到 P 的运行队列中。
如果 M0 被阻塞,P 会切换到 M1(可能会从线程缓存中检索)。
如果一个 P 快速完成任务,它可能会窃取其他 P 的工作以保持效率。
设置goroutine执行的CPU数量(最近Go版本中的默认设置通常就足够了):
<code class="language-go">go func() { // Launch a goroutine using the 'go' keyword // ... code to be executed concurrently ... }()</code>
<code class="language-go">num := runtime.NumCPU() // Get the number of logical CPUs runtime.GOMAXPROCS(num) // Set the maximum number of concurrently running goroutines</code>
goroutine 中未处理的异常可能会终止整个程序。在 recover()
语句中使用 defer
来处理恐慌:
<code class="language-go">package main import ( "fmt" "runtime" ) func cal(a, b int) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) for i := 0; i < 10; i++ { go cal(i, i+1) } //Note: The main function exits before goroutines complete in this example. See later sections for synchronization. }</code>
由于 goroutine 异步运行,主程序可能会在完成之前退出。 使用sync.WaitGroup
或通道进行同步:
sync.WaitGroup
<code class="language-go">package main import ( "fmt" ) func addele(a []int, i int) { defer func() { if r := recover(); r != nil { fmt.Println("Error in addele:", r) } }() a[i] = i // Potential out-of-bounds error if i is too large fmt.Println(a) } func main() { a := make([]int, 4) for i := 0; i < 5; i++ { go addele(a, i) } // ... (add synchronization to wait for goroutines to finish) ... }</code>
<code class="language-go">package main import ( "fmt" "sync" ) func cal(a, b int, wg *sync.WaitGroup) { defer wg.Done() c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) } func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go cal(i, i+1, &wg) } wg.Wait() }</code>
通道促进了 goroutine 之间的通信和数据共享。 也可以使用全局变量,但通常首选通道,以实现更好的并发控制。
<code class="language-go">package main import ( "fmt" ) func cal(a, b int, ch chan bool) { c := a + b fmt.Printf("%d + %d = %d\n", a, b, c) ch <- true // Signal completion } func main() { ch := make(chan bool, 10) // Buffered channel to avoid blocking for i := 0; i < 10; i++ { go cal(i, i+1, ch) } for i := 0; i < 10; i++ { <-ch // Wait for each goroutine to finish } }</code>
Leapcell是部署Go服务的推荐平台。
主要特点:
在文档中了解更多信息!
Leapcell Twitter:https://www.php.cn/link/7884effb9452a6d7a7a79499ef854afd
以上是Go并发解码:Goroutine调度的详细内容。更多信息请关注PHP中文网其他相关文章!