Golang併發模型:輕鬆入門流水線模型
Golang作為一個實用主義的程式語言,非常注重效能,在語言特性上天然支援併發,它有多種併發模型,通過流水線模型系列文章,你會更好的使用Golang併發特性,提高你的程式效能。
這篇文章主要介紹流水線模型的流水線概念,後面文章介紹流水線模型的FAN-IN和FAN-OUT,最後介紹下如何合理的關閉流水線的協程。
Golang的併發核心思路
Golang併發核心思路是關注資料流動。資料流動的過程交給channel,資料處理的每個環節都交給goroutine,把這些流程畫起來,有始有終形成一條線,那就能構成流水線模型。
但我們先從簡單的入手。
從一個簡單的流水線入手
流水線並不是什麼新奇的概念,它能極大的提高生產效率,在當代社會流水線非常普遍,我們用的幾乎任何產品(手機、電腦、汽車、水杯),都是從流水線上生產出來的。以汽車為例,整個汽車流水線要經過幾百個組裝點,而在某個組裝點只組裝固定的零部件,然後傳遞給下一個組裝點,最終一臺完整的汽車從流水線上生產出來。
Golang的併發模型靈感其實都來自我們生活,對軟體而言,高的生產效率就是高的效能。
在Golang中,流水線由多個階段組成,每個階段之間通過channel連線,每個節點可以由多個同時執行的goroutine組成。
從最簡單的流水線入手。下圖的流水線由3個階段組成,分別是A、B、C,A和B之間是通道aCh
bCh
,A生成資料傳遞給B,B生成資料傳遞給C。
流水線中,第一個階段的協程是生產者,它們只生產資料。最後一個階段的協程是消費者,它們只消費資料。下圖中A是生成者,C是消費者,而B只是中間過程的處理者。
舉個例子,設計一個程式:計算一個整數切片中元素的平方值並把它打印出來。非併發的方式是使用for遍歷整個切片,然後計算平方,列印結果。
我們使用流水線模型實現這個簡單的功能,從流水線的角度,可以分為3個階段:
- 遍歷切片,這是生產者。
- 計算平方值。
- 列印結果,這是消費者。
下面這段程式碼:
producer()
負責生產資料,它會把資料寫入通道,並把它寫資料的通道返回。square()
負責從某個通道讀數字,然後計算平方,將結果寫入通道,並把它的輸出通道返回。main()
負責啟動producer和square,並且還是消費者,讀取suqre的結果,並打印出來。
package main
import (
"fmt"
)
func producer(nums ...int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for _, n := range nums {
out <- n
}
}()
return out
}
func square(inCh <-chan int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for n := range inCh {
out <- n * n
}
}()
return out
}
func main() {
in := producer(1, 2, 3, 4)
ch := square(in)
// consumer
for ret := range ch {
fmt.Printf("%3d", ret)
}
fmt.Println()
}
結果:
➜ awesome git:(master) ✗ go run hi.go
1 4 9 16
這是一種原始的流水線模型,這種原始能讓我們掌握流水線的思路。
流水線的特點
- 每個階段把資料通過channel傳遞給下一個階段。
- 每個階段要建立1個goroutine和1個通道,這個goroutine向裡面寫資料,函式要返回這個通道。
- 有1個函式來組織流水線,我們例子中是main函式。
如果你沒了解過流水線,建議自己把以上的程式寫一遍,如果遇到問題解決了,那才真正掌握了流水線模型的思路。
完整示例程式碼
本文所有程式碼都在倉庫,可檢視完整示例程式碼:https://github.com/Shitaibin/golang_pipeline_step_by_step
併發系列文章推薦
下一篇,我將介紹流水線模型的FAN-IN、FAN-OUT,歡迎關注。
關注公眾號,獲取最新Golang文章。 一起學Golang-分享有料的Go語言技術
- 如果這篇文章對你有幫助,不妨關注下我的Github,有文章會收到通知。
- 本文作者:大彬
- 如果喜歡本文,隨意轉載,但請保留此原文連結:http://lessisbetter.site/2018/11/16/golang-introduction-to-pipeline/