Golang 哪些情况会导致协程泄露?
Posted 知其黑、受其白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang 哪些情况会导致协程泄露?相关的知识,希望对你有一定的参考价值。
阅读目录
Golang 哪些情况会导致协程泄露?
- 通道阻塞
- 锁阻塞
- 等待阻塞
协程泄露是指,在程序运行过程中,有一些协程由于某些原因,无法正常退出。
协程的运行是需要占用内存和 CPU 时间的,一旦这种协程越来越多,会导致内存无端被浪费,CPU 时间片被占用,程序会越来越卡。
1 通道使用不当
通道和协程是 Go 的两大杀器,配合好了是非常香的,但要是没用好,就会造成协程泄露。
下面是常见的一些通道使用不当导致的协程泄露例子:
发送了却没全接收
package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
func main()
for i := 0; i < 4; i++
queryAll()
fmt.Printf("goroutines: %d\\n", runtime.NumGoroutine())
func queryAll() int
ch := make(chan int)
for i := 0; i < 3; i++
go func()
ch <- query()
()
return <-ch
func query() int
n := rand.Intn(100)
time.Sleep(time.Duration(n) * time.Millisecond)
return n
没发送却有人在接收
package main
import (
"fmt"
"runtime"
"time"
)
func main()
defer func()
fmt.Println("goroutines: ", runtime.NumGoroutine())
()
var ch chan struct
go func()
ch <- struct
()
time.Sleep(time.Second)
初始化通道却没分配内存
package main
import (
"fmt"
"runtime"
"time"
)
func main()
defer func()
fmt.Println("goroutines: ", runtime.NumGoroutine())
()
var ch chan int
go func()
<-ch
()
time.Sleep(time.Second)
2 锁使用不当
加互斥锁后没有解锁
加了互斥锁后,若没有释放,其他 Goroutine 再想获取锁就会阻塞。
因此在加了互斥锁后,可以下意识加个 defer mutex.Unlock()
,养成编码习惯。
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main()
total := 0
defer func()
time.Sleep(time.Second)
fmt.Println("total: ", total)
fmt.Println("goroutines: ", runtime.NumGoroutine())
()
var mutex sync.Mutex
for i := 0; i < 10; i++
go func()
mutex.Lock()
// 正常加锁后,可以下意识加个 defer mutex.Unlock()
total += 1
()
同步锁使用不当
如下例子中,wg.Add
的数量与 wg.Done
的数量不一致,就会导致 wg.Wait
阻塞。
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func handle(v int)
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < v; i++
wg.Done()
wg.Wait()
func main()
defer func()
fmt.Println("goroutines: ", runtime.NumGoroutine())
()
go handle(3)
time.Sleep(time.Second)
3 慢等待未响应
在下面这个例子中,发送一个 http 请求的时候,如果网络非常的卡,导致这个请求一直没有收到响应,那么就会一直进行 for 循环,不断创建新的协程。
package main
import (
"fmt"
"net/http"
"runtime"
"time"
)
func main()
for
go func()
_, err := http.Get("https://www.xxx.com/")
if err != nil
fmt.Printf("http.Get err: %v\\n", err)
// do something...
()
time.Sleep(time.Second * 1)
fmt.Println("goroutines: ", runtime.NumGoroutine())
以上是关于Golang 哪些情况会导致协程泄露?的主要内容,如果未能解决你的问题,请参考以下文章
协程的简单操作,你都知道哪些?Golang如何实现协程交替打印?