每个源文件可以定义自己的不带参数的init函数,来设置它所需的状态。init是在程序包中所有变量声明都被初始化,以及所有被导入的程序包中的变量初始化之后才被调用。
除了用于无法通过声明来表示的初始化以外,init函数的一个常用法是在真正执行之前进行验证或者修复程序状态的正确性。
【规则4.1】一个文件只定义一个init函数。
【规则4.2】一个包内的如果存在多个init函数,不能有任何的依赖关系。
注意如果包内有多个init,每个init的执行顺序是不确定的。
4.5 defer的使用原则
【建议4.10】如果函数存在多个返回的地方,则采用defer来完成如关闭资源、解锁等清理操作。
说明:Go的defer语句用来调度一个函数调用(被延期的函数),在函数即将返回之前defer才被运行。这是一种不寻常但又很有效的方法,用于处理类似于不管函数通过哪个执行路径返回,资源都必须要被释放的情况。典型的例子是对一个互斥解锁,或者关闭一个文件。
【建议4.11】defer会消耗更多的系统资源,不建议用于频繁调用的方法中。
【建议4.12】避免在for循环中使用defer。
说明:一个完整defer过程要处理缓存对象、参数拷贝,以及多次函数调用,要比直接函数调用慢得多。
错误示例:实现一个加解锁函数,解锁过程使用defer处理。这是一个非常小的函数,并且能够预知解锁的位置,使用defer编译后会使处理产生很多无用的过程导致性能下降。
1. var lock sync.Mutex
2. func testdefer() {
3. lock.Lock()
4. defer lock.Unlock()
5. }
6.
7. func BenchmarkTestDefer(b *testing.B) {
8. for i := 0; i < b.N; i++ {
9. testdefer()
10. }
11. }
12.
13. // 耗时结果
14. BenchmarkTestDefer 10000000 211 ns/op
推荐做法:如果能够明确函数退出的位置,可以选择不使用defer处理。保证功能不变的情况下,性能明显提升,是耗时是使用defer的1/3。
1. var lock sync.Mutex
2. func testdefer() {
3. lock.Lock()
4. lock.Unlock() // 【修改】去除defer
5. }
6.
7. func BenchmarkTestDefer(b *testing.B) {
8. for i := 0; i < b.N; i++ {
9. testdefer()
10. }
11. }
12.
13. // 耗时结果
14. BenchmarkTest" 30000000 43.5 ns/op
4.6 Goroutine使用原则
【规则4.3】确保每个goroutine都能退出。
说明:Goroutine是Go并行设计的核心,在实现功能时不可避免会使用到,执行goroutine时会占用一定的栈内存。
启动goroutine就相当于启动了一个线程,如果不设置线程退出的条件就相当于这个线程失去了控制,占用的资源将无法回收,导致内存泄露。
错误示例:示例中ready()启动了一个goroutine循环打印信息到屏幕上,这个goroutine无法终止退出。
1. package main
2.
3. import (
4. "fmt"
5. "time"
6. )
7.
8. func ready(w string, sec int) {
9. go func() { // 【错误】goroutine启动之后无法终止
10. for {
11. time.Sleep(time.Duration(sec) * time.Second)
12. fmt.Println(w, "is ready! ")
13. }
14. }()
15. }
16.
17. func main() {
18. ready("Tea", 2)
19. ready("Coffee", 1)
20. fmt.Println("I'm waiting")
21. time.Sleep(5 * time.Second)
22. }
推荐做法:对于每个goroutine都需要有退出机制,能够通过控制goroutine的退出,从而回收资源。通常退出的方式有:
使用标志位的方式;
信号量;
通过channel通道通知;
注意:channel是一个消息队列,一个goroutine获取signal后,另一个goroutine将无法获取signal,以下场景下每个channel对应一个goroutine
1. package main
2.
3. import (
4. "fmt"
5. "time"
6. )
7.
8. func ready(w string, sec int, signal chan struct{}) {
9. go func() {
10. for {
11. select {
12. case <-time.Tick(time.Duration(sec) *
除了用于无法通过声明来表示的初始化以外,init函数的一个常用法是在真正执行之前进行验证或者修复程序状态的正确性。
【规则4.1】一个文件只定义一个init函数。
【规则4.2】一个包内的如果存在多个init函数,不能有任何的依赖关系。
注意如果包内有多个init,每个init的执行顺序是不确定的。
4.5 defer的使用原则
【建议4.10】如果函数存在多个返回的地方,则采用defer来完成如关闭资源、解锁等清理操作。
说明:Go的defer语句用来调度一个函数调用(被延期的函数),在函数即将返回之前defer才被运行。这是一种不寻常但又很有效的方法,用于处理类似于不管函数通过哪个执行路径返回,资源都必须要被释放的情况。典型的例子是对一个互斥解锁,或者关闭一个文件。
【建议4.11】defer会消耗更多的系统资源,不建议用于频繁调用的方法中。
【建议4.12】避免在for循环中使用defer。
说明:一个完整defer过程要处理缓存对象、参数拷贝,以及多次函数调用,要比直接函数调用慢得多。
错误示例:实现一个加解锁函数,解锁过程使用defer处理。这是一个非常小的函数,并且能够预知解锁的位置,使用defer编译后会使处理产生很多无用的过程导致性能下降。
1. var lock sync.Mutex
2. func testdefer() {
3. lock.Lock()
4. defer lock.Unlock()
5. }
6.
7. func BenchmarkTestDefer(b *testing.B) {
8. for i := 0; i < b.N; i++ {
9. testdefer()
10. }
11. }
12.
13. // 耗时结果
14. BenchmarkTestDefer 10000000 211 ns/op
推荐做法:如果能够明确函数退出的位置,可以选择不使用defer处理。保证功能不变的情况下,性能明显提升,是耗时是使用defer的1/3。
1. var lock sync.Mutex
2. func testdefer() {
3. lock.Lock()
4. lock.Unlock() // 【修改】去除defer
5. }
6.
7. func BenchmarkTestDefer(b *testing.B) {
8. for i := 0; i < b.N; i++ {
9. testdefer()
10. }
11. }
12.
13. // 耗时结果
14. BenchmarkTest" 30000000 43.5 ns/op
4.6 Goroutine使用原则
【规则4.3】确保每个goroutine都能退出。
说明:Goroutine是Go并行设计的核心,在实现功能时不可避免会使用到,执行goroutine时会占用一定的栈内存。
启动goroutine就相当于启动了一个线程,如果不设置线程退出的条件就相当于这个线程失去了控制,占用的资源将无法回收,导致内存泄露。
错误示例:示例中ready()启动了一个goroutine循环打印信息到屏幕上,这个goroutine无法终止退出。
1. package main
2.
3. import (
4. "fmt"
5. "time"
6. )
7.
8. func ready(w string, sec int) {
9. go func() { // 【错误】goroutine启动之后无法终止
10. for {
11. time.Sleep(time.Duration(sec) * time.Second)
12. fmt.Println(w, "is ready! ")
13. }
14. }()
15. }
16.
17. func main() {
18. ready("Tea", 2)
19. ready("Coffee", 1)
20. fmt.Println("I'm waiting")
21. time.Sleep(5 * time.Second)
22. }
推荐做法:对于每个goroutine都需要有退出机制,能够通过控制goroutine的退出,从而回收资源。通常退出的方式有:
使用标志位的方式;
信号量;
通过channel通道通知;
注意:channel是一个消息队列,一个goroutine获取signal后,另一个goroutine将无法获取signal,以下场景下每个channel对应一个goroutine
1. package main
2.
3. import (
4. "fmt"
5. "time"
6. )
7.
8. func ready(w string, sec int, signal chan struct{}) {
9. go func() {
10. for {
11. select {
12. case <-time.Tick(time.Duration(sec) *