1. 程式人生 > >07 - 獲取URL, 併發 goroutine和channel

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