go 调度模型
抢占式调度
1
2
3
4
5
6
7
8
9
10
11
12
13func main() {
// runtime.GOMAXPROCS(-1) 返回核数
for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
go func(num int) {
fmt.Printf("goroutine[%v] started\n", num)
for {
}
}(i)
}
// 执行不到
time.Sleep(time.Second)
fmt.Println("Hello")
}在函数调用时会检查当前 goroutine 是否应该让出资源
1 | func complexFunction(level int) { |
- 简单的函数可能会被编译器内联
1 | func simpleFunc() { |
channel
- 无缓存 vs 有缓存
无缓冲的通道保证进行发送和接收的 goroutine 会在同一时间进行数据交换;有缓冲的 通道没有这种保证
可以理解为无缓存的通道是容量为 0 的缓冲通道。
一个例子:
1 | c := make(chan int, 1) |
- 判断 chan 的状态
1 | c := make(chan int, 10) |
关闭
- 不可以重复关闭
- 一般由写入方关闭
- 不关闭也可以,会被 GC 回收
- 关闭后可读不可写
- 关闭后读不会阻塞
利用 channel 产生质数 (不是很好理解)
1 | package main |
range 的问题
- 迭代的变量是同一个
1 | var m = map[string]int{ |
- 迭代过程删除
1 | s := []int{1, 2, 3, 4, 5} |
select
- 随机选择一个准备好的通道
操作超时
1
2
3
4
5
6
7
8
9
10func main() {
var c = make(chan struct{}, 10)
// close(c)
select {
case <-time.After(time.Second):
fmt.Println("TIMEOUT")
case <-c:
fmt.Println("OK")
}
}定时任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18func main() {
closed := make(chan struct{})
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
time.AfterFunc(3*time.Second, func() {
close(closed)
})
for {
select {
case <-closed:
fmt.Println("closed")
return
case <-ticker.C:
fmt.Println("triggered")
}
}
}
struct
- 不是类
- 不能继承
- 不能定义成员方法
- 可以定义带接收器的函数
- 接收器可以为 nil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15type Student struct {
name string
}
func (s *Student) Name() string {
if s == nil {
return "invalid student"
}
return s.name
}
func main() {
var stu *Student = nil
fmt.Println(stu.Name())
}
匿名嵌入
- 复用嵌入结构体的属性
- 复用把嵌入结构体作为接收器的函数
- 问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package main
import "fmt"
type Human struct {
id int
name string
}
func (h *Human) SetID(id int) { // = func setId(h *Human, id int)
h.id = id
}
type Student struct {
Human
id int64
}
func main() {
stu := &Student{
Human: Human{},
}
fmt.Printf("%+v\n", stu)
stu.SetID(2333)
fmt.Printf("%+v\n", stu)
}
//
&{Human:{id:0 name:} id:0}
&{Human:{id:2333 name:} id:0}
interface
- interface 的 nil 判断
1
2
3
4
5
6
7
8
9
10func isNil(i interface{}) bool {
// return reflect.ValueOf(i).IsNil() // true
return i == nil // false
}
func main() {
var stu *Student
fmt.Println(stu == nil) // true
fmt.Println(isNil(stu))
}
interface 会保存数据类型和数据的值对于 nil 而言,它的类型是 nil,值是 nil
对于传入的 stu 而言,它的类型是 *Student,值是 nil
error 和 panic
- 尽量不用 panic
处理 panic 的姿势
1
2
3
4
5
6
7
8func NoPanic() {
defer func() {
if err := recover(); err != nil {
fmt.Println("safe")
}
}()
panic("BOOM")
}recover 的限制,只能处理当前 goroutine
1
2
3
4
5
6
7
8
9
10
11func NoPanic() {
defer func() {
if err := recover(); err != nil {
fmt.Println("safe")
}
}()
// panic at other goroutine.
go panic("BOOM")
// wait new goroutine running
time.Sleep(time.Second)
}
defer
- 执行顺序和栈一样
1 | func SayHello() { |
- 即使 panic 也会执行,这也是为什么可以在 defer 中 recover 的原因
1 | func SayHello() { |
常见使用姿势
释放资源
1
2
3
4
5
6resp, err := http.Get("https://shopee.tw")
if err != nil {
// handle error
}
defer resp.Body.Close()
// handle response释放锁
1
2lock.Lock()
defer lock.Unlock()回滚事务
1
2
3
4
5
6
7
8
9
10
11
12func updateSomething() {
tx := db.Begin()
defer tx.Rollback() // 如果提交了,这一句不会执行
rows, err := tx.Query("do something")
if err != nil {
// handle error
return
}
handleRows(rows)
tx.Commit()
}
通过 defer 执行的函数,他的参数是在 defer 时计算好的,而不是在执行时计算的