读阻塞
- 期待从channel读数据,结果没有goroutine往进写数据
func channelTest() {
var c = make(chan int)
//1个协程向channel中写数据
go func() {
<- c
fmt.Println("g1 receive succeed")
time.Sleep(1 * time.Second)
}()
//10个协程丛channel读数据
for i := 0; i < 10; i++ {
go func() {
c <- 1
fmt.Println("g2 send succeed")
time.Sleep(1 * time.Second)
}()
}
//会有读的9个协程阻塞得不到释放
time.Sleep(10 * time.Second)
}
4. goroutine导致的内存泄漏
4.1 申请过多的goroutine
例如在for循环中申请过多的goroutine来不及释放导致内存泄漏
4.2 goroutine阻塞
4.2.1 I/O问题
I/O连接未设置超时时间,导致goroutine一直在等待,代码会一直阻塞。
4.2.2 互斥锁未释放
goroutine无法获取到锁资源,导致goroutine阻塞
//协程拿到锁未释放,其他协程获取锁会阻塞
func mutexTest() {
mutex := sync.Mutex{}
for i := 0; i < 10; i++ {
go func() {
mutex.Lock()
fmt.Printf("%d goroutine get mutex", i)
//模拟实际开发中的操作耗时
time.Sleep(100 * time.Millisecond)
}()
}
time.Sleep(10 * time.Second)
}
4.2.3 死锁
当程序死锁时其他goroutine也会阻塞
func mutexTest() {
m1, m2 := sync.Mutex{}, sync.RWMutex{}
//g1得到锁1去获取锁2
go func() {
m1.Lock()
fmt.Println("g1 get m1")
time.Sleep(1 * time.Second)
m2.Lock()
fmt.Println("g1 get m2")
}()
//g2得到锁2去获取锁1
go func() {
m2.Lock()
fmt.Println("g2 get m2")
time.Sleep(1 * time.Second)
m1.Lock()
fmt.Println("g2 get m1")
}()
//其余协程获取锁都会失败
go func() {
m1.Lock()
fmt.Println("g3 get m1")
}()
time.Sleep(10 * time.Second)
}
4.2.4 waitgroup使用不当
waitgroup的Add、Done和wait数量不匹配会导致wait一直在等待
5. slice 引起的内存泄漏
当两个slice 共享地址,其中一个为全局变量,另一个也无法被GC;
append slice 后一直使用,没有进行清理。
var a []int
func test(b []int) {
a = b[:3]
return
}
6. 数组的值传递
由于数组时Golang的基本数据类型,每个数组占用不通的内存空间,生命周期互不干扰,很难出现内存泄漏的情况,但是数组作为形参传输时,遵循的时值拷贝,如果函数被多个goroutine调用且数组过大时,则会导致内存使用激增。
//统计nums中target出现的次数
func countTarget(nums [1000000]int, target int) int {
num := 0
for i := 0; i < len(nums) && nums[i] == target; i++ {
num++
}
return num
}
因此对于大数组放在形参场景下通常使用切片或者指针进行传递,避免短时间的内存使用激增。