Golang併發模型 輕鬆入門流水線FAN模式

2021-09-07 08:47:33 字數 4353 閱讀 5262

前一篇文章《golang併發模型:輕鬆入門流水線模型》,介紹了流水線模型的概念,這篇文章是流水線模型高階,介紹fan-in和fan-out,fan模式可以讓我們的流水線模型更好的利用golang併發,提高軟體效能。但fan模式不一定是萬能,不見得能提高程式的效能,甚至還不如普通的流水線。我們先介紹下fan模式,再看看它怎麼提公升效能的,它是不是萬能的。

golang的併發模式靈感來自現實世界,這些模式是通用的,毫無例外,fan模式也是對當前世界的模仿。以汽車組裝為例,汽車生產線上有個階段是給小汽車裝4個輪子,可以把這個階段任務交給4個人同時去做,這4個人把輪子都裝完後,再把汽車移動到生產線下乙個階段。這個過程中,就有任務的分發,和任務結果的收集。其中任務分發是fan-out,任務收集是fan-in。

我們這次試用fan-out和fan-in,解決《golang併發模型:輕鬆入門流水線模型》中提到的問題:計算乙個整數切片中元素的平方值並把它列印出來。

fan模式流水線示例:

package main

import

("fmt"

"sync"

)func

producer

(nums ...

int)

chan

int}()

return out

}func

square

(inch

chan

int)

chan

int}()

return out

}func

merge

(cs ...

chan

int)

chan

int}

wg.add(

len(cs)

)// fan-in

for_

, c :=

range cs

// 錯誤方式:直接等待是bug,死鎖,因為merge寫了out,main卻沒有讀

// wg.wait()

// close(out)

// 正確方式

gofunc()

()return out

}func

main()

fmt.

println()

}

3個squre協程併發執行,結果順序是無法確定的,所以你得到的結果,不一定與下面的相同。

➜  awesome git:

(master) ✗ go run hi.go1

4169

普通流水線:

// hi_******.go

package main

import

("fmt"

)func

producer

(n int

)chan

int}()

return out

}func

square

(inch

chan

int)

chan

int}()

return out

}func

main()

}

使用fan模式的流水線:

// hi_fan.go

package main

import

("sync"

"time"

)func

producer

(n int

)chan

int}()

return out

}func

square

(inch

chan

int)

chan

int}()

return out

}func

merge

(cs ...

chan

int)

chan

int}

wg.add(

len(cs)

)// fan-in

for_

, c :=

range cs

// 錯誤方式:直接等待是bug,死鎖,因為merge寫了out,main卻沒有讀

// wg.wait()

// close(out)

// 正確方式

gofunc()

()return out

}func

main()

}

多次測試,每次結果近似,結果如下:

➜  awesome git:(master) ✗ time go run hi_******.go

go run hi_******.go 0.17s user 0.18s system 3% cpu 10.389 total

➜ awesome git:(master) ✗

➜ awesome git:(master) ✗ time go run hi_fan.go

go run hi_fan.go 0.17s user 0.16s system 7% cpu 4.288 total

也可以使用benchmark進行測試,看2個型別的執行時間,結論相同。為了節約篇幅,這裡不再介紹,方法和結果貼在gist了,想看的朋友瞄一眼,或自己動手搞搞。

fan模式可以提高併發的效能,那我們是不是可以都使用fan模式?

不行的,因為fan模式不一定能提公升效能。

依然使用之前的問題,再次修改下**,其他不變:

簡單版流水線修改**:

// hi_******.go

func

square

(inch

chan

int)

chan

int}()

return out

}func

main()

}

fan模式流水線修改**:

// hi_fan.go

package main

import

("sync"

)func

square

(inch

chan

int)

chan

int}()

return out

}func

main()

}

結果,可以跑多次,結果近似:

➜  awesome git:(master) ✗ time go run hi_******.go    

go run hi_******.go 9.96s user 5.93s system 168% cpu 9.424 total

➜ awesome git:(master) ✗ time go run hi_fan.go

go run hi_fan.go 23.35s user 11.51s system 297% cpu 11.737 total

從這個結果,我們能看到2點。

既然fan模式不一定能提高效能,如何優化?

不同的場景優化不同,要依具體的情況,解決程式的瓶頸。

我們當前程式的瓶頸在fan-in,squre函式很快就完成,merge函式它把3個資料寫入到1個通道的時候出現了瓶頸,適當使用帶緩衝通道可以提高程式效能,再修改下**

結果:

➜  awesome git:(master) ✗ time go run hi_fan_buffered.go 

go run hi_fan_buffered.go 19.85s user 8.19s system 323% cpu 8.658 total

使用帶快取通道後,程式的效能有了較大提公升,cpu利用率提高到323%,提公升了8%,執行時間從11.7降低到8.6,降低了26%。fan模式的特點很簡單,相信你已經掌握了,如果記不清了看這裡,本文所有**在該github倉庫。

fan模式很有意思,並且能提高golang併發的效能,如果想以後運用自如,用到自己的專案中去,還是要寫寫自己的demo,快去實踐一把。

本文所有**都在倉庫,可檢視完整示例**:

如果這篇文章對你有幫助,不妨關注下我的github,有文章會收到通知。

網路流輕鬆入門

以下為自己理解,各位巨佬權當 看好啦,若有誤請指出,蟹蟹 水流出於源,聚於匯。想象許多條錯綜交錯的小溪從源頭流出,匯聚到另一頭的大海中,網路流描述的問題就是,小溪流入的寬度各不相同,換句話說就是分段限流,假設源頭流入的水量是無限的,問水源一次無限的水灌入後能有多少匯聚到大海?靈魂畫手登場 如果要手算...

Golang併發模型 select高階

最近公司工作有點多,golang的select高階就這樣被拖沓啦,今天堅持把時間擠一擠,把吹的牛皮補上。前一篇文章 golang併發模型 輕鬆入門select 介紹了select的作用和它的基本用法,這次介紹它的3個高階特性。nil的通道永遠阻塞 如何跳出for selectselect 阻塞 當c...

Actor併發模型入門

使用scala,基於akka的actor併發模型。importakka.actor.actor importakka.actor.props importakka.actor.actorsystem importakka.routing.roundrobinpool 定義乙個封閉的特質 sealed...