Go 语言最大的特征之一,就是对并发编程的支持,接下来我们将简单理解一下并发
一、goroutine 获取URL
06 - 获取URL 中简单地获取了 http 的信息,接下来我们将会同时去获取所有的 URL ,所以这个程序的总执行时间不会超过执行时间最长的那一个任务,前面的 urlfetch 程序执行时间则是所有任务执行时间之和。 urlfetchall 程序只会打印获取的内容大小和经过的时间,不会像之前那样打印获取的内容。
相关源代码如下:
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"time"
)
func main() {
start := time.Now()
ch := make(chan string)
for _, url := range os.Args[1:] {
go fetch(url, ch)
}
for range os.Args[1:] {
fmt.Println(<-ch)
}
fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}
func fetch(url string, ch chan<- string) {
start := time.Now()
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprint(err)
return
}
//ioutil.Discard 为io.Writer 类型,是类似于/dev/null
// 像一个无底洞
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
if err != nil {
ch <- fmt.Sprintf("while reading %s: %v", url, err)
return
}
secs := time.Since(start).Seconds()
ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)
}
运行路径:
$GOPATH/src/gopl/ch1/urlfetch2
运行命令:
$ go run urlfetchall.go https://www.baidu.com https://www.qq.com https://www.sina.com.cn
运行结果:
程序分析
1. goroutinue
goroutinue ,本质上就是协程。但有两点不同:
- goroutinue 可以实现并行,也就是说,多个协程可以在多个处理器同时跑。而协程同一时刻只能在一个处理器上跑(把宿主语言想象成单线程的就好了)。
- goroutinue 之间的通信是通过 channel ,而协程的通信是通过 yield 和 resume() 操作。
- 运行 goroutinue ,只需要在函数的调用前面加一个 go
go doingSth()
2. channel
在 java 的世界里,并发主要是靠锁住临界资源(共享内存)来保证同步的。而 channel 则是 goroutinues 之间进行通信的利器。
channel 可以形象比喻为工厂里的传送带,一头的生产者 goroutine 往传输带放东西,另一头的消费者 goroutinue 则从输送带取东西。 channel 实际上是一个 有类型的消息队列 ,遵循先进先出的特点。
-
channel的操作符号
ch <- ele 表示ele被发送给channel ch;
ele2 <- ch 表示从channel ch取一个值,然后赋给ele2 -
阻塞式channel
channel默认是没有缓冲区的,没有buff的channel只能容纳一个元素,也就是说,通信是阻塞的。send操作必须等到有消费者accept才算完成。
-
带有buff的channel
带有buff的channel则可以非阻塞容纳N个元素。发送数据到buffed channel不会被阻塞,除非channel已满;同样的,从buffed channel取数据也不会被阻塞,除非channel空了。这有点像java的ConcurrentLinkedQueue。
3. io.Discard
它是一个 io.Writer 类型的变量, 是一个类似于 /dev/null 的无底洞
总结
- goroutinue 和 channel 的简单理解, 后续并发编程将会更加详细的学习
- io.Discard 的理解
- 练习: 找一个数据量比较大的网站,用本小节中的程序调研网站的缓存策略,对每个URL执行两遍请求,查看两次时间是否有较大的差别,并且每次获取到的响应内容是否一致,修改本节中的程序,将响应结果输出,以便于进行对比。 源代码路径:$GOPATH/src/gopl/ch1/urlfetch2