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 如何防止 goroutine 泄露

Golang 如何防止 goroutine 泄露

golang协程调度模式解密

协程的简单操作,你都知道哪些?Golang如何实现协程交替打印?

协程的简单操作,你都知道哪些?Golang如何实现协程交替打印?

(四十二)golang--管道