避坑指南

此开源图书由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!
}

最后更新于

这有帮助吗?