避坑指南
此开源图书由ithaiq原创,创作不易转载请注明出处
1. Channel与Goroutine泄露
当channel不恰当使用时,就可能导致Goroutine发生永久阻塞从而造成资源泄露。
1.1. 发送不接收
对于一个已满的channel(buffered channel 容量已满或是 unbuffered channel),继续向其发送数据将会导致当前goroutine阻塞。为了避免这种情况需要使用其他机制通知发送者。
1.2. 接收不发送
与前述情况相反,若接收者一直在一个不会再产生数据的 channel 上等待,将导致其所在routine 阻塞而泄露。 在Go中从一个 closed channel 读取数据:
不会阻塞且获取对应类型的零值
for-range将退出
v, ok := <-ch
中ok将为false
所以可以利用上述性质通知接收方结束数据读取。
1.3. nil channel
向 nil channel 发送和接收数据都将会导致阻塞。 这种情况可能在我们定义 channel 时忘记初始化的时候发生。
func main() {
defer func() {
time.Sleep(time.Second)
fmt.Println("num of routines: ", runtime.NumGoroutine())
}()
var ch chan intgo func() {
<-ch
// ch<-o
}()
}
2. 跳出for-switch或for-select
没有指定标签的 break 只会跳出 switch/select 语句, 若不能使用 return 语句跳出的话,可为 break 跳出标签指定的代码块。 注意 goto
虽然也能跳转到指定位置,但依旧会再次进入 for-switch,死循环。
// break 配合 label 跳出指定代码块
func main() {
loop:
for {
switch {
case true:
fmt.Println("breaking out...")
// break // 死循环,一直打印 breaking out...
break loop
}
}
fmt.Println("out...")
}
3. 闭包中的for迭代变量
for 语句中的迭代变量在每次迭代中都会重用,即 for 中创建的闭包函数 接收到的参数始终是同一个变量,所以在 goroutine 开始执行时都会得到同一个迭代值
4. 循环内的defer
对 defer 延迟执行的函数,会在调用它的函数结束时执行,而不是在调用它的语句块结束时执行,注意区分开。
5. defer函数的参数值
defer 只会延迟其后函数的执行,而不会延迟函数的参数的求值,若希望延迟其参数 求值,通常会加上一层匿名函数。
func main() {
var i = 1
times := func(num int) int {
return num * 2
}
defer fmt.Println("resultA: ", times(i))
defer func() {
fmt.Println("resultB: ", func() int { return i * 2 }())
}()
i++
// Output:// resultB: 4// resultA: 2
}
6. nil interface 和 nil interface 值
Golang中 interface 类型变量的实现中包含值与类型,只有两者都为nil时该变量才为nil。 下面是一个错误的范例。
type Foo interface {
Bar()
}
type FooImpl struct {
num int
}
func (f FooImpl) Bar() { fmt.Println(f.num) }
func GenFoo(num int) (Foo, error) {
var f FooImpl
if num != 0 {
f = &FooImpl{num}
}
return f, nil
}
func main() {
f, err := GenFoo(0)
if err != nil {
return
}
f.Bar() // Panic!
}
最后更新于
这有帮助吗?