Go 语言并发编程的学习笔记
Goroutine
os.Exit
(log.Fatal
会调用这个) 的进行退出时,注册的 defer
函数不会执行
启动 Goroutine 的时候要问两个问题:
- 它什么时候结束
- 如何让它结束
- 一定要做超时控制
启动 Goroutine 的时候要尽量交给调用者执行
1
2
|
# ListDirectory 如果要启动 Goroutine,那么也应该给调用者提供停止该 Goroutine 的方法,这里提供了一个回调函数参数,让调用者能够停止该 Goroutine
func ListDirectory(dir string, fn func(string))
|
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
type Tracker struct {
ch chan string
stop chan struct{}
}
func NewTracker() *Tracker {
return &Tracker{
ch: make(chan string, 10),
}
}
func (t *Tracker) Event(ctx context.Context, data string) error {
select {
case t.ch <- data:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func (t *Tracker) Run() {
for data := range t.ch {
time.Sleep(1 * time.Second)
fmt.Println(data)
}
close(t.stop)
}
// Shutdown 函数用来关闭运行着的 Goroutine,并且还可以设置关闭的超时时间
func (t *Tracker) Shutdown(ctx context.Context) {
close(t.ch)
select {
case <-t.stop:
// 关闭 Goroutine 成功
case <-ctx.Done():
// 关闭 Goroutine 超时
}
}
// 这个例子就体现了使用 Goroutine 的原则:
// 1. 调用者在创建了 Goroutine 后,一定能够控制它什么时候退出
// 2. 知道 Goroutine 什么时候退出
// 3. 一定要处理 Goroutine 的超时错误
// 4. 将并发逻辑交给调用者,即让调用者来创建 Goroutine
func main() {
tr := NewTracker()
go tr.Run()
_ = tr.Event(context.Background(), "test")
_ = tr.Event(context.Background(), "foo")
_ = tr.Event(context.Background(), "bar")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
tr.Shutdown(ctx)
}
|
锁的用法
- 最晚加锁,最早释放
- 锁里面的代码越段越好,越简单越好
- 加多把锁的情况下,注意不要产生死锁
Atomic
- 能用 atomic 尽量用 atomic,它比锁快一个数量级
Copy On Write
进程在 fork 了子进程后,内核并不会立刻复制子进程的内存页。当父子进程中任意一个进程修改了内存后,内核内核会复制被修改的进程页,这样来节省内存资源。
参考链接: Redis-关于RDB的几点顿悟-COW
- Golang 业务中的 Copy On Write
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
|
// Go 业务中的 Copy On Write
// 当有配置需要更改时,先复制一份,在复制份上执行修改,然后再利用 atomic.Value 这个原子操作修改配置的指针
func main() {
type Map map[string]string
var m atomic.Value
var mu sync.Mutex // 写协程之间的互斥锁
read := func(key string) (val string) {
m1 := m.Load().(Map)
return m1[key]
}
write := func(key, value string) {
mu.Lock()
defer mu.Unlock()
m1 := m.Load().(Map)
m2 := make(Map)
for k, v := range m1 {
m2[k] = v
}
m2[key] = value
m.Store(m2)
}
_, _ = read, write
}
|
Sync.Pool
sync.Pool 的场景是用来保存和复用临时对象,以减少内存分配,降低 GC 压力。
放进 Pool 中的对象,会说不准在什么时候 GC 掉。
Context
文章作者
bwangel
上次更新
2021-01-10
许可协议
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处