现在要完成一个非常简单的任务,给定很多图片的 URL
,将它们下载到本地,你会怎么做?
Sequential
如果你想也不想,就写下了下面这种代码,说明你是一个能干活的程序员,但这并不意味着你是一个优秀的程序员。下面的代码虽然没有什么致命的错误,但是效率非常低,程序运行时 CPU
大部分时间都在等待网络 I/O
而处于空闲状态,导致 CPU
的利用率非常低。
1 | for idx, task := range tasks { |
Parallel
这种完全由相互独立的子任务组成的任务,被称为 embarrassingly parallel ,这种问题可以通过并行的方法提高程序的性能,而且并行程度越高,性能越好,在程序中我们通过添加 go
这个关键字来创建多个 goroutine
,使多个子任务并发执行,以减少网络 I/O
延迟,提高 CPU
的利用率 。
1 | var wg sync.WaitGroup |
Parallel with Semaphore
但是通过创建多个 goroutine
提高并行度从而提高性能的方法有其局限性,例如一次性创建过多的网络连接,超出了进程打开文件的数量限制时,程序就会报错。所以我们需要限制单位时间内并行程序的个数,通过信号量( semaphore
)来限制并行度。在 golang
中,我们可以使用容量为 n 的 buffered channel
来模拟 counting semaphore
,这样以来在每个子任务执行前需要通过 sem <- struct{}{}
来获取执行许可,如果 sem
通道已满,说明当前已经有n个子任务正在执行,该操作就会阻塞等待,直到通道空闲。当任务执行完成后,使用 <-sem
释放执行许可。
1 | sem := make(chan struct{}, 8) // 8 jobs at once |