Go語言的Http并發

coolono 8年前發布 | 21K 次閱讀 并發 HTTP Go語言 Google Go/Golang開發

我們為什么需要并發程序?

  • 資源利用率 :從整個程序的執行角度來看,程序執行時可以看作是對輸入的數據進行計算處理然后輸出到特定的設備中。如果這條流程線完全是串行執行的話,當其中的一個環節正在執行的時候其他環節就不能工作。這就意味著一旦輸入阻塞,即IO等待讀入數據那么已讀入的數據也不能得到處理,已處理的數據也不能輸出,這就造成了CPU的閑置。而如果這三個步驟可以并發執行的話即使IO在等待輸入CPU仍然可以對已在內存中的數據做計算處理,結果也可以正常輸出。這就提高了CPU的利用率,不會因為輸入輸出的阻塞導致CPU的計算能力被浪費。
  • 時間 :很多任務彼此之間并沒有什么關聯,當有充分的資源可以使用時,它們可以同時被執行,與串行地執行任務相比,這樣既可以充分利用現有資源,提高資源的利用率,同時又可以減少任務完成的總時間,可以節省出更多的時間處理接下來的任務。
  • 公平性 :對于同優先級的任務來說,它們應該能夠受到計算機資源的同等待遇。如果是串行執行的話就意味著有先有后,這就造成了任務處理的不公平性,并發就可以很好地解決這個問題,它們或是同時在不同的CPU上執行,或是在單個CPU上交替執行,保證了任務應該享有的公平性。
  • 簡便性 :當有多種類型的任務執行時,為每種任務單獨編寫程序比編寫混雜在一起的所有任務的處理程序要簡單的多。試想當我們在處理多種事情時,把每種任務都分配給單獨的人員比起把每種任務都平均分配然后讓相應人員都處理所有種類的任務相比,效率肯定要高的多。一方面是因為一直做一件事會做的越來越熟,更重要的是可以專心做一件事而不用受到其他事情的干擾,這一點想必已經工作的朋友一定深有體會。

所有 的 高級 編程 語言 在 單 核心 的 機器 上 運行 。Go是 現代 編程 語言 , 它能夠是我們充分利用 機器 的 所有 內核 。

  • 什么時候適合使用并發編程呢?
    任何事情的優勢就決定了它在什么情況下適用。正如上面” 為什么要使用并發編程 ”中所說,并發編程最大的優勢就是提高程序的運行速度和資源利用率,而如果串行執行的程序在這兩方面并不受到限制的話就沒有必要使用并發編程了,而如果在這兩方面遇到了瓶頸需要有所突破或者你就是個該死的極致性能追求者的話,就要考慮你的程序場景是不是符合下面這些并發編程的用武之地了:
    • 任務會阻塞線程,導致之后的代碼不能執行 :比如一邊從文件中讀取,一邊進行大量計算的情況
    • 任務執行時間過長,可以劃分為分工明確的子任務 :比如分段下載
    • 任務間斷性執行 :比如上報crash,日志打印
    • 任務本身需要協作執行 :比如生產者消費者問題
    • ……
  • 在實際的并發編程時都需要注意什么呢?
    上面所說的并發編程的風險大概就是并發編程的注意點,更具體一點,從并發的編程角度來看,我個人更愿意將并發編程分為三部分:
    • 多線程的并發執行 :這是并發編程的核心,研究的是如何保證任務在不同線程中并發執行從而提高程序的運行速度
    • 線程間的通信 :線程的執行雖然是并發的,但是他們所執行的任務并不一定是獨立的,它研究的就是如何實現任務所在線程之間的高效、可靠的通信
    • 線程間對共享狀態的同步與互斥 :線程之間會共享一些對象,我們稱之為狀態,當多線程同時讀寫某個共享狀態時可能會因不恰當的執行時序而造成程序邏輯的混亂,如何保證共享狀態的互斥(即保證任意時刻某個共享狀態只能由單個線程訪問)和同步(當前線程的值都是上一線程執行完后的最新的值)之后的文章也是圍繞這三方面來展開的,其中多線程的并發執行是基礎,畢竟如果沒有并發,也就談不上線程間通信和共享狀態了。然而就多線程并發執行單方面僅僅是解決性能的問題,而如果沒有線程間通信和對共享狀態的保護,恐怕連最最基本的正確性都不能保證了。因此,這三方面對于并發編程來說缺一不可,任何一項的短板都不能讓我們成功編寫優秀的并發程序。

引入依賴包

package main
 import (
 "fmt" 
 "io/ioutil" 
 "os" 
 "net/http" 
 "time" 
 )
  • “fmt” is for I/O
  • “io/ioutil” is for reading the HTTP response body
  • “os” is for accesing command line arguments
  • “time” is for printing time data
  • “net/http” is for making HTTP requests

我們在寫一個 叫做 MakeRequest 的 function 用來做http的并發請求

funcMakeRequest(urlstring, chchan<- string) {
 start := time.Now()
 resp, _ := http.Get(url)
 secs := time.Since(start).Seconds()
 body, _ := ioutil.ReadAll(resp.Body)
 ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
}

makerequest 需要2個參數,一個string類型的url,一個channel 類型的ch,我們都知道在Go 語言中go rotines 是通過channel進行通信的。我們這里為每一個url請求開啟一個go routine,只要請求結束 go routine 將返回的數據寫入到channel中去。在main 方法中會吧 channel中存儲的數據打印出來。

func main() {
 start := time.Now()
 ch := make(chan string)
 for _, url := range os.Args[1:] {
 go MakeRequest(url, ch)
 }
 for range os.Args[1:] {
 fmt.Println(<-ch)
 }
 fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}
  1. Note down the start time
  2. Create a channel ch
  3. For each URL of command line arguments launch a go co-routine
  4. For each URL read the channel for result
  5. Calculate time difference between beginning and end
$ gobuildconcurrent.go

我們在使用Python3來順序程序與go 的并發做一個比較

$ timepython3 -c "import requests;print(len(requests.get('http://localhost:8000').text));print(len(requests.get('http://localhost:8000').text));print(len(requests.get('http://localhost:8000').text))"
$ time ./concurrenthttp://localhost:8000 http://localhost:8000 http://localhost:8000

Time Go 1.6 Python 3
real 0.00s 0.18s
user 0.00s 0.02s
cpu 0.006s 0.197s

我們可以看到結果 Go 需要的時間比Python 少很多。

來自: https://xiequan.info/go語言的http并發/

 

 本文由用戶 coolono 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!