前一篇文章《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...