從三體質(zhì)子鎖定地球科技 簡(jiǎn)析Golang的并發(fā)
好吧鹿榜,我承認(rèn)這個(gè)標(biāo)題是騙你進(jìn)來的命爬,那么來都來了施敢,看完再走吧周荐。
——
通常情況下,我們都會(huì)覺得golang有著不錯(cuò)的性能僵娃。那么這個(gè)不錯(cuò)的性能是如何得出的呢概作?我覺得大概有以下幾點(diǎn)
-
靜態(tài)語言VS動(dòng)態(tài)語言
靜態(tài)語言通常有著比解釋型語言更好的性能. 靜態(tài)語言需要預(yù)先編譯成二進(jìn)制機(jī)器語言,執(zhí)行效率自然比解釋型語言的解釋器邊解釋邊執(zhí)行效率要高出不少.
所以默怨,單從執(zhí)行效率上看仆嗦,C/C++/C#/Java/Golang 等靜態(tài)要比 Python/Javascript/Ruby等 動(dòng)態(tài)語言要高出不少
-
GC(Garbage Collection)
相比Java的虛擬機(jī)JVM或C# 的.NET Framework 提供的垃圾回收機(jī)制,Golang有著更高的效率.
詳細(xì)上先壕,Golang的GC咱沒做過仔細(xì)研究,.NET 的GC有著引用計(jì)數(shù)以及三代的垃圾處理機(jī)制谆甜,實(shí)際上垃僚,微軟在.net core里也做了一定的優(yōu)化,但總歸來說规辱,Golang的GC更加的輕量谆棺,執(zhí)行效率也更高(沒有虛擬機(jī)呀).
-
并發(fā)VS并行
并發(fā):同一時(shí)刻,通過調(diào)度罕袋,來回切換交替運(yùn)行多個(gè)任務(wù)改淑,給人的感覺是同時(shí)運(yùn)行的
并行:同一時(shí)刻,多個(gè)任務(wù)同時(shí)在運(yùn)行.
回到標(biāo)題浴讯,在科幻小說<<三體>>中朵夏,三體星人通過發(fā)送到地球的一個(gè)高維展開過的質(zhì)子就能攪亂地球的所有粒子對(duì)撞機(jī)的規(guī)律, 那么,三體的質(zhì)子應(yīng)該是并發(fā)來處理的而不是并行的來執(zhí)行干擾工作.
并發(fā)通過切換CPU運(yùn)行時(shí)間片榆纽,能達(dá)到非常高的執(zhí)行效率仰猖。尤其是在IO密集的計(jì)算中.
Golang 使用goroutine 來支持并發(fā), goroutine說白了其實(shí)就是協(xié)程捏肢,但是它比線程輕量。線程的建立需要較多的資源消耗饥侵,但是, 執(zhí)行g(shù)oroutine只需極少的棧內(nèi)存鸵赫。
Golang中的并發(fā)
雖然并發(fā)并非golang所特有, 比如python就有對(duì)協(xié)程的支持躏升。但是就便捷性上來講辩棒,Golang從語言層面就支持了并發(fā),使用起來也更加方便.
goroutine通過在函數(shù)前加 go關(guān)鍵字實(shí)現(xiàn), 一個(gè)官方的例子.
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(time.Millisecond * 10)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
執(zhí)行結(jié)果:
hello
world
hello
world
world
hello
world
hello
world
hello
Channel
goroutine的函數(shù)運(yùn)行在相同的地址空間膨疏,那么就必須做好內(nèi)存同步工作一睁。相比其它語言的多線程同步鎖,golang 使用channel 來進(jìn)行數(shù)據(jù)通信成肘, 顯得非常的優(yōu)秀和高效卖局。 channel 可以發(fā)送也可以接受channel 類型的值. 我們可以使用make來定義一個(gè)channel.
var ch_i = make(chan int)
var ch_s = make(chan string)
var ch_st = make(chan struct{})
舉個(gè)栗子。
我們要計(jì)算一個(gè)數(shù)組所有元素的累加和双霍, 我們可以把相應(yīng)的數(shù)組拆成若干個(gè)goroutine并發(fā)執(zhí)行.
package main
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:2], c)
go sum(a[2:4], c)
go sum(a[4:], c)
x, y, z := <-c, <-c, <-c
fmt.Println(x, y, z, x+y+z)
}
執(zhí)行結(jié)果:
4 -1 9 12
三個(gè)協(xié)程并發(fā)計(jì)算結(jié)果并通過channel 來傳遞數(shù)據(jù).
通過 c <- total砚偶, 把計(jì)算結(jié)果傳遞給channel, 通過 x, y, z := <-c, <-c, <-c 來接受channel的值.
帶緩存的channel
默認(rèn)的channel是不帶緩存的洒闸,但我們也可以創(chuàng)建帶緩存的channel. 在channel創(chuàng)建的時(shí)候可以指定緩存大小染坯。
在緩存大小范圍內(nèi),就可以一直往channel里塞丘逸。直到塞滿单鹿,阻塞。再直到有人從channel取出深纲,釋放緩存仲锄。
舉個(gè)栗子。
我們定義一個(gè)帶緩存的channel湃鹊,用來存儲(chǔ)斐波那契數(shù)組.
package main
import "fmt"
func fibonacci(n int, c chan int) {
x, y := 1, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
}
func main() {
c := make(chan int, 5)
go fibonacci(cap(c), c)
x, y, z, a, b := <-c, <-c, <-c, <-c, <-c
fmt.Println(x, y, z, a, b)
}
運(yùn)行結(jié)果:
1 1 2 3 5
遍歷/關(guān)閉 channel
上面的例子略顯丑儒喊,我們可以通過range 函數(shù)來遍歷緩存里的值.
package main
import "fmt"
func fibonacci(n int, c chan int) {
x, y := 1, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
運(yùn)行結(jié)果:
1
1
2
3
5
8
13
21
34
55
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
C:/Users/trump/go/src/github.com/goroutine-test/main.go:16 +0xb9
系統(tǒng)報(bào)了一個(gè)錯(cuò)誤,意思是說所有的goroutines 都沒了币呵。原因就是怀愧,range 函數(shù)在channel 沒有關(guān)閉的時(shí)候會(huì)一直運(yùn)行,10個(gè)讀完了以后余赢,沒有新的數(shù)值進(jìn)來芯义,它就死了。
解決辦法:
在fibonacci函數(shù)中關(guān)閉通道妻柒。
package main
import "fmt"
func fibonacci(n int, c chan int) {
x, y := 1, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
總結(jié)
以上就是個(gè)人對(duì)Golang 并發(fā)的一些粗淺理解扛拨。
雖然您可能被騙了,但如果文章對(duì)你有所幫助蛤奢,也是值了鬼癣。謝謝陶贼。