07 - 获取URL, 并发 goroutine和channel

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 ,本质上就是协程。但有两点不同:

  1. goroutinue 可以实现并行,也就是说,多个协程可以在多个处理器同时跑。而协程同一时刻只能在一个处理器上跑(把宿主语言想象成单线程的就好了)。
  2. goroutinue 之间的通信是通过 channel ,而协程的通信是通过 yieldresume() 操作。
  • 运行 goroutinue ,只需要在函数的调用前面加一个 go
    go doingSth()
    

2. channel

java 的世界里,并发主要是靠锁住临界资源(共享内存)来保证同步的。而 channel 则是 goroutinues 之间进行通信的利器。


channel 可以形象比喻为工厂里的传送带,一头的生产者 goroutine 往传输带放东西,另一头的消费者 goroutinue 则从输送带取东西。 channel 实际上是一个 有类型的消息队列 ,遵循先进先出的特点。

  1. channel的操作符号

    ch <- ele 表示ele被发送给channel ch;
    ele2 <- ch 表示从channel ch取一个值,然后赋给ele2

  2. 阻塞式channel

    channel默认是没有缓冲区的,没有buff的channel只能容纳一个元素,也就是说,通信是阻塞的。send操作必须等到有消费者accept才算完成。

  3. 带有buff的channel

    带有buff的channel则可以非阻塞容纳N个元素。发送数据到buffed channel不会被阻塞,除非channel已满;同样的,从buffed channel取数据也不会被阻塞,除非channel空了。这有点像java的ConcurrentLinkedQueue。

3. io.Discard

它是一个 io.Writer 类型的变量, 是一个类似于 /dev/null 的无底洞

总结

  • goroutinuechannel 的简单理解, 后续并发编程将会更加详细的学习
  • io.Discard 的理解
  • 练习: 找一个数据量比较大的网站,用本小节中的程序调研网站的缓存策略,对每个URL执行两遍请求,查看两次时间是否有较大的差别,并且每次获取到的响应内容是否一致,修改本节中的程序,将响应结果输出,以便于进行对比。 源代码路径:$GOPATH/src/gopl/ch1/urlfetch2

猜你喜欢

转载自blog.csdn.net/xiaomiCJH/article/details/86302938