在 golang 中可以运行成千上万个 goroutine,你不必担心,因为同样的线程在进程中,如果运行那么多数量,则可能引发问题,因为 golang 中的协程是 golang 的 runtime 在管理,尽管协程也存在调度和上下文切换,但在用户态即可完成。
不过需要注意一点的是,在 golang 中并发运行多个 goroutine 时,应该做好 故障恢复,否则会影响进程也退出。
以下就是个影响示例:
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
/*
time: 2024-07-28T17:31:37.8146231+08:00, task: [0] is running...
task: 0 exit.
time: 2024-07-28T17:31:37.8146231+08:00, task: [1] is running...
task: 1 exit.
time: 2024-07-28T17:31:37.8146231+08:00, task: [3] is running...
task: 7 exit.
time: 2024-07-28T17:31:37.8146231+08:00, task: [2] is running...
task: 2 exit.
task: 8 exit.
task: 5 exit.
main exit.
panic: cannot handle task id = 5
*/
func main() {
tasksCnt := 10
for i := 0; i < tasksCnt; i++ {
wg.Add(1)
go gogo(i)
}
wg.Wait()
fmt.Println("main exit.")
}
func gogo(idx int) {
defer func() {
defer wg.Done()
fmt.Printf("task: %d exit.\n", idx)
}()
fmt.Printf("time: %v, task: [%d] is running...\n", time.Now().Format(time.RFC3339Nano), idx)
if idx == 5 {
panic(fmt.Sprintf("cannot handle task id = %d\n", idx))
}
}
从结果可以看出,当 idx == 5 时会触发 panic,此时 idx = 5 的协程就退出了,而其他协程都还没运行结束,最后进程自己也挂了,这就是 子协程异常退出会影响 进程 退出。
所以,如果我们在子协程中做好 try-catch,异常子协程的问题就不会影响到进程,以下是示例:
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
/*
time: 2024-07-28T17:42:50.8298977+08:00, task: [4] is running...
task: 4 exit.
time: 2024-07-28T17:42:50.8304225+08:00, task: [2] is running...
task: 2 exit.
time: 2024-07-28T17:42:50.8304225+08:00, task: [6] is running...
task: 6 exit.
time: 2024-07-28T17:42:50.8304225+08:00, task: [5] is running...
task: 5 exit.
Recover err, cannot handle task id = 5
time: 2024-07-28T17:42:50.8304225+08:00, task: [0] is running...
task: 0 exit.
time: 2024-07-28T17:42:50.8298977+08:00, task: [1] is running...
task: 1 exit.
time: 2024-07-28T17:42:50.8304225+08:00, task: [7] is running...
task: 7 exit.
time: 2024-07-28T17:42:50.8298977+08:00, task: [9] is running...
task: 9 exit.
time: 2024-07-28T17:42:50.8304225+08:00, task: [8] is running...
task: 8 exit.
time: 2024-07-28T17:42:50.8304225+08:00, task: [3] is running...
task: 3 exit.
main exit.
*/
func main() {
tasksCnt := 10
for i := 0; i < tasksCnt; i++ {
wg.Add(1)
go gogo(i)
}
wg.Wait()
fmt.Println("main exit.")
}
func gogo(idx int) {
defer func() {
if err := recover(); err != nil {
fmt.Println("Recover err,", err)
}
}()
defer func() {
defer wg.Done()
fmt.Printf("task: %d exit.\n", idx)
}()
fmt.Printf("time: %v, task: [%d] is running...\n", time.Now().Format(time.RFC3339Nano), idx)
if idx == 5 {
panic(fmt.Sprintf("cannot handle task id = %d\n", idx))
}
}