goroutine 资料和内容很多,这里不用进行说明和讲解,只要知道go语言的核心在于 communicate by channel , rather than by share memory 就好了。这里记录遇到的一些小坑。
1. panic影响主线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import (
"fmt"
"sync"
)
func main() {
fmt.Println("[main] routine start..")
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
panicExample()
wg.Done()
}()
wg.Wait()
fmt.Println("[main] routine stop..")
}
func panicExample() {
arr := make([]int, 0, 3)
fmt.Println(arr[10])
}
|
返回结果:
[main] routine start..
panic: runtime error: index out of range [10] with length 3
goroutine 25 [running]:
main.panicExample()
/Users/nimo/go/src/testGo/main.go:25 +0xd3
main.main.func1(0xc00044aa60)
/Users/nimo/go/src/testGo/main.go:13 +0x25
created by main.main
/Users/nimo/go/src/testGo/main.go:12 +0xc7
Process finished with exit code 2
这里使用 index out of range 错误,直接导致了整体线程的崩溃。 当然,如果这里是gin,也是一样的,这是后台应用所无法接受的。所以一般需要做一些recover的封装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
func main() {
fmt.Println("[main] routine start..")
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println(time.Now().String()+gconv.String(r)+string(debug.Stack()), errors.New("go routine error"))
}
wg.Done()
}()
panicExample()
}()
wg.Wait()
fmt.Println("[main] routine stop..")
}
|
2. goroutine 不能直接使用Loop variable的问题(Loop variable ‘XXX’ captured by func literal )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
package main
import (
"errors"
"fmt"
"runtime/debug"
"sync"
"time"
"github.com/gogf/gf/util/gconv"
)
func main() {
animals := []string{"cat", "dog", "chicken"}
wg := sync.WaitGroup{}
for _, animal := range animals {
fmt.Println("for loop animal:" + animal)
wg.Add(1)
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println(time.Now().String()+gconv.String(r)+string(debug.Stack()), errors.New("go routine error"))
}
}()
defer wg.Done()
fmt.Println("go routine animal:" + animal)
}()
}
wg.Wait()
fmt.Println("[main] stop")
}
|
输出结果为:
for loop animal:cat
for loop animal:dog
for loop animal:chicken
go routine animal:chicken
go routine animal:chicken
go routine animal:chicken
[main] stop
结果是错误的,go运训goroutine引用外部的变量。这个没有问题,有问题的在于主线程和routine的启动时间不同, 主线程的循环跑完了之后、各个routine才刚刚开始跑,所以拿到的结果是最后的结果。
所以这里需要注意,传递给routine的变量必须是不能变的。至少在主线程结束前不能变。
对于这种情况,解决问题有两种:一种是单独赋值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
func main() {
animals := []string{"cat", "dog", "chicken"}
wg := sync.WaitGroup{}
for _, animal := range animals {
fmt.Println("for loop animal:" + animal)
wg.Add(1)
animal0 := animal
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println(time.Now().String()+gconv.String(r)+string(debug.Stack()), errors.New("go routine error"))
}
}()
defer wg.Done()
fmt.Println("go routine animal:" + animal0)
}()
}
wg.Wait()
fmt.Println("[main] stop")
}
|
另一种是传递参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
c main() {
animals := []string{"cat", "dog", "chicken"}
wg := sync.WaitGroup{}
for _, animal := range animals {
fmt.Println("for loop animal:" + animal)
wg.Add(1)
go func(animalInner string) {
defer func() {
if r := recover(); r != nil {
fmt.Println(time.Now().String()+gconv.String(r)+string(debug.Stack()), errors.New("go routine error"))
}
}()
defer wg.Done()
fmt.Println("go routine animal:" + animalInner)
}(animal)
}
wg.Wait()
fmt.Println("[main] stop")
}
|