1.6 并发获取多个URL
Go最令人感兴趣和新颖的特点是支持并发编程。这是一个大话题,第8章和第9章将专门讨论,所以此处只是简单了解一下Go主要的并发机制、goroutine和通道(channel)。
下一个程序fetchall和前一个一样获取URL的内容,但是它并发获取很多URL内容,于是这个进程使用的时间不超过耗时最长时间的获取任务,而不是所有获取任务总的时间。这个版本的fetchall丢弃响应的内容,但是报告每一个响应的大小和花费的时间:
这有一个例子:
goroutine是一个并发执行的函数。通道是一种允许某一例程向另一个例程传递指定类型的值的通信机制。main函数在一个goroutine中执行,然后go语句创建额外的goroutine。
main函数使用make创建一个字符串通道。对于每个命令行参数,go语句在第一轮循环中启动一个新的goroutine,它异步调用fetch来使用http.Get获取URL内容。io.Copy函数读取响应的内容,然后通过写入ioutil.Discard输出流进行丢弃。Copy返回字节数以及出现的任何错误。每一个结果返回时,fetch发送一行汇总信息到通道ch。main中的第二轮循环接收并且输出那些汇总行。
当一个goroutine试图在一个通道上进行发送或接收操作时,它会阻塞,直到另一个goroutine试图进行接收或发送操作才传递值,并开始处理两个goroutine。本例中,每一个fetch在通道ch上发送一个值(ch <- expression),main函数接收它们(<-ch)。由main来处理所有的输出确保了每个goroutine作为一个整体单元处理,这样就避免了两个goroutine同时完成造成输出交织所带来的风险。
练习1.10:找一个产生大量数据的网站。连续两次运行fetchall,看报告的时间是否会有大的变化,调查缓存情况。每一次获取的内容一样吗?修改fetchall将内容输出到文件,这样可以检查它是否一致。
练习1.11:使用更长的参数列表来尝试fetchall,例如使用alexa.com排名前100万的网站。如果一个网站没有响应,程序的行为是怎样的?(8.9节会通过复制这个例子来描述响应的机制。)