【译文】原文地址
本文是原文作者在学习go总结的一些结论分享给新手们供参考学习。
内存:栈和堆
- Go运行时会为每个Goroutine创建1个栈。
- Goroutine每次退出时,Go运行时不会都去清理栈,运行时会标记栈为invalid,这样其他程序和routine可以申明使用该栈。
- Go运行时能够观察到变量被引用,因此会将其内存转移到堆上,使goroutine继续可以访问这部分内存。这被称为逃逸分析。
- 栈内存分配规则:向下共享一般还是留在栈;向上共享一般会逃逸到堆上。
只有编译器知道什么时候特殊情况出现。为了获取准确的信息可以在编译程序时添加上gcflags参数:
go build -gcflags=”-m -l” program.go
- 什么时候变量会逃逸到堆上呢?
- 如果函数退出了变量还被引用
- 当一个变量在栈中占有太大内存
- 编译的时候,编译器不清楚占有内存大小
Goroutines
- 每个程序至少有一个Goroutine:main主协程,在程序启动的时候自动创建并运行。
- 一个Go协程就是一个并发执行的函数。注意和并行的区别。
- 协程并不是操作系统线程,更抽象一层。
- Go遵循fork-join模型来创建和等待goroutines。
- 防止内存泄漏:通过创建信号在父协程和子协程之间同步,达到对子协程的终止。通常这个信号是一个只读的channel名为done。父协程传递这个channel给子协程,当要终止子协程的时候关闭这个channel即可。
- 如果一个协程负责创建一个新协程,它也必须负责确保能停止新协程。
- 为了在协程里更好的处理错误,创建一个结构体来封装可能的结果和错误。返回一个这个结构的channel。
Sync package
WaitGroup
使用这个函数来等待其他一系列的并发操作完成,并且你可以不用关心并发操作的结果或通过其他方式获取结果。
Mutex和RWMutex
锁定一部分代码只让一个Goroutine可执行。最佳实践是在锁的下一行增加一个defer语句释放锁。
RWMutex提供一个更细粒度的控制读写权限。
channel
- channel提供信息流管道服务;值可以在channel中传递到下游。
- 双向和单向可选
- channel在Go中是阻塞的。意味着当一个channel满了,任何想写入的goroutine必须等待直到有空间;另一方面如果一个channel是空的,任何想从中读取值的goroutine必须等待直到至少有一个值写入。
- 读取会发送两个值分别是value和ok。如果channel被关闭了会返回channel值类型的默认值和ok是false。使用range读取channel会检查ok值。
- 拥有channel的Goroutine应该实例化channel,执行写操作或将channel传给其他goroutine,关闭channel;或者暴露给其他协程来处理。
Select和for-select语句
- 在select语句中,所有的分支在计算值时是同时的,第一个就绪的被执行。如果有多个选择,go运行时做随机选择。
- 使用 <- time.after(n * teme.second)退出select,如果没有分支能满足条件的情况。
- 如果没有分支准备就绪,default分支会被执行。和for循环结合实现多次检查其他选择。
for {
select {
case <-done:
return
default:
// Do non-preemptable work
}
}
Pipeline
当创建流水线时使用生成器函数将输入转化为channel
func IntGenerator(done <-chan interface{}, integers ...int) <-chan int {
intStream := make(chan int)
go func() {
defer close(intStream)
for _, i := range integers {
select {
case <-done:
return
case intStream <- I:
}
}
}()
return intStream
}
切片申明和初始化
var arr []string // 申明切片, 值为nil
arr := make([]string, 3) //申明并初始化值为 [“”,””,””]
接口
编译的时候检查
接口的实现是隐式的并在运行时检查。如果没有遵守interface的规范,在运行时会报错。
接收器很重要
package main
import (
“fmt”
)
type Animal interface {
Speak() string
}
type Dog struct {
}
func (d Dog) Speak() string {
return “Woof!”
}
type Cat struct {
}
func (c *Cat) Speak() string {
return “Meow!”
}
func main() {
animals := []Animal{Dog{}, Cat{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
// Output
./prog.go:26:32: cannot use Cat literal (type Cat) as type Animal in slice literal:
Cat does not implement Animal (Speak method has pointer receiver)
接口隔离原则
Go代码原则:接收接口,返回结构体
网友评论