并行编程

并行设计原则

Keep yourself busy or do the work yourself

如果依赖于协程的结果返回,不如直接由自己操作

Leave concurrency to the caller

让并行让调用者去控制

1
2
3
func ListDirectory(dir string)([]string, error)

func ListDirectory(dir string) chan string

第一种方式是同步调用,会导致阻塞,如果目录很大,可能会导致读取时间很长,并且分配一个很大的空间。

第二种方式返回chan string,将通过该chan传递目录。当通道关闭时,这表示不在读取目录。由于在ListDirectory返回后发生通道的填充,ListDirectory可能内部启动goroutine填充通道。但是也有两个问题

  1. 无法区分空目录和错误

  2. 调用者必须从中读取数据,即使它可能已经有了想要的答案
    解决方案

1
func ListDirectory(dir string, fn func(string))

防止goroutine的泄漏,将异步执行函数的决定权交给该函数的调用方。

Never start a goroutine without knowing when it will stop

协程泄漏案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func search(term string) (string, error) {
time.Sleep(200 * time.Millisecond)
return "some value", nil
}

func process(term string) error {
var ctx context.Context
var ch = make(chan result)
go func() {
record, err := search(term)
ch <- result{record, err}
}()

select {
case <-ctx.Done():
return errors.New("canceled")
case result := <-ch:
if result.err != nil {
return result.err
}
}
return nil
}

当ctx.Done()先执行后,如果ch不是buffer channel,没有任何人获取到ch上的内容,这样会导致协程的阻塞。

开启一个协程后,必须要思考:

  1. 他什么时候会结束
  2. 有没有办法结束它

要保证创建的goroutine的声明周期,写的人停止channel


并行编程
https://www.zengzx.xyz/2023/03/07/01.知识架构/01.Go/01.基础/09.并行编程/
作者
Eden
发布于
2023年3月7日
许可协议