golang学习笔记21——golang协程管理及sync.WaitGroup的使用
- 推荐学习文档
- golang应用级os框架,欢迎star
- golang应用级os框架使用案例,欢迎star
- 案例:基于golang开发的一款超有个性的旅游计划app经历
- golang实战大纲
- golang优秀开发常用开源库汇总
- 想学习更多golang知识,这里有免费的golang学习笔记专栏
文章目录
- 引言
- 协程管理中的常见问题
- 1.协程泄漏
- 2.协程过多导致资源耗尽
- 解决方案
- 1.避免协程泄漏
- 2.限制协程数量
- 总结
引言
Golang 中的协程(goroutine)为并发编程带来了极大的便利,但在实际开发中,如果对协程管理不当,也会产生一系列问题。本文将深入探讨这些问题,并结合代码示例给出相应的解决方案。
协程管理中的常见问题
1.协程泄漏
- 协程在执行过程中,如果由于某些原因(如阻塞在某个通道上、陷入死锁等)没有正常退出,就会导致协程泄漏。大量的协程泄漏会耗尽系统资源,如内存等。
- 示例代码:
package main
import (
"fmt"
"time"
)
func leakyGoroutine() {
// 这个协程会一直阻塞,导致协程泄漏
<-make(chan int)
}
func main() {
for i := 0; i < 10; i++ {
go leakyGoroutine()
}
// 主线程休眠一段时间,让协程有机会执行
time.Sleep(5 * time.Second)
fmt.Println("程序结束,但协程泄漏了")
}
2.协程过多导致资源耗尽
- 创建过多的协程而没有进行有效的限制和管理,会使系统资源(如 CPU 时间片、内存等)被大量占用,从而影响系统的性能和稳定性。
- 示例代码:
package main
import (
"fmt"
"runtime"
"sync"
)
func manyGoroutines() {
var wg sync.WaitGroup
for i := 0; i < 100000; i++ {
wg.Add(1)
go func() {
// 模拟协程执行一些简单的操作
for j := 0; j < 1000; j++ {
_ = j
}
wg.Done()
}()
}
wg.Wait()
}
func main() {
before := runtime.NumGoroutine()
manyGoroutines()
after := runtime.NumGoroutine()
fmt.Printf("创建前协程数量: %d, 创建后协程数量: %d\n", before, after)
}
解决方案
1.避免协程泄漏
- 合理使用通道和超时机制
- 对于可能阻塞的通道操作,可以设置超时时间,避免协程无限制地等待。
- 代码示例:
package main
import (
"fmt"
"time"
)
func nonLeakyGoroutine() {
// 创建一个带超时的通道
timeout := time.After(3 * time.Second)
ch := make(chan int)
go func() {
// 模拟可能阻塞的操作
time.Sleep(5 * time.Second)
ch <- 1
}()
select {
case <-ch:
fmt.Println("协程正常接收数据")
case <-timeout:
fmt.Println("操作超时,协程退出")
}
}
func main() {
for i := 0; i < 10; i++ {
go nonLeakyGoroutine()
}
// 主线程休眠一段时间
time.Sleep(5 * time.Second)
fmt.Println("程序结束,没有协程泄漏")
}
- 避免死锁
- 在多个协程之间进行同步和通信时,要确保资源的获取和释放顺序正确,避免出现死锁导致协程无法退出。
2.限制协程数量
- 使用信号量(Semaphore)
- 通过信号量来限制同时执行的协程数量。
- 代码示例:
package main
import (
"fmt"
"sync"
)
// 定义信号量
var semaphore = make(chan struct{}, 10)
func limitedGoroutine() {
// 获取信号量
semaphore <- struct{}{}
defer func() {
// 释放信号量
<-semaphore
}()
// 协程执行的操作
fmt.Println("协程执行中...")
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
limitedGoroutine()
wg.Done()
}()
}
wg.Wait()
fmt.Println("所有协程执行完毕")
}
总结
在 Go 语言中,协程管理是并发编程的关键部分。通过避免协程泄漏和合理限制协程数量等措施,可以有效地提高程序的性能和稳定性,充分发挥 Go 语言在并发编程方面的优势。
关注我看更多有意思的文章哦!👉👉