日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Go语言并发与并行

發布時間:2025/6/15 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go语言并发与并行 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先,并行!=并發, 兩者是不同的

Go語言的goroutines、信道和死鎖

goroutine


Go語言中有個概念叫做goroutine, 這類似我們熟知的線程,但是更輕。
以下的程序,我們串行地去執行兩次loop函數:

func loop() {for i := 0; i < 10; i++ {fmt.Printf("%d ", i)} }func main() {loop()loop() }

毫無疑問,輸出會是這樣的:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

下面我們把一個loop放在一個goroutine里跑,我們可以使用關鍵字go來定義并啟動一個goroutine:

func main() {go loop() // 啟動一個goroutineloop() }

這次的輸出變成了:

0 1 2 3 4 5 6 7 8 9

可是為什么只輸出了一趟呢?明明我們主線跑了一趟,也開了一個goroutine來跑一趟啊。

原來,在goroutine還沒來得及跑loop的時候,主函數已經退出了。

main函數退出地太快了,我們要想辦法阻止它過早地退出,一個辦法是讓main等待一下:

func main() {go loop()loop()time.Sleep(time.Second) // 停頓一秒 }

這次確實輸出了兩趟,目的達到了。

可是采用等待的辦法并不好,如果goroutine在結束的時候,告訴下主線說“Hey, 我要跑完了!”就好了, 即所謂阻塞主線的辦法,回憶下我們Python里面等待所有線程執行完畢的寫法:

for thread in threads:thread.join()

是的,我們也需要一個類似join的東西來阻塞住主線。那就是信道

信道

信道是什么?簡單說,是goroutine之間互相通訊的東西。類似我們Unix上的管道(可以在進程間傳遞消息), 用來goroutine之間發消息和接收消息。其實,就是在做goroutine之間的內存共享。

使用make來建立一個信道:那如何向信道存消息和取消息呢? 一個例子:

func main() {var messages chan string = make(chan string)go func(message string) {messages <- message // 存消息}("Ping!")fmt.Println(<-messages) // 取消息 }

默認的,信道的存消息和取消息都是阻塞的 (叫做無緩沖的信道,不過緩沖這個概念稍后了解,先說阻塞的問題)。

也就是說, 無緩沖的信道在取消息和存消息的時候都會掛起當前的goroutine,除非另一端已經準備好。

比如以下的main函數和foo函數:

var ch chan int = make(chan int)func foo() {ch <- 0 // 向ch中加數據,如果沒有其他goroutine來取走這個數據,那么掛起foo, 直到main函數把0這個數據拿走 }func main() {go foo()<- ch // 從ch取數據,如果ch中還沒放數據,那就掛起main線,直到foo函數中放數據為止 }
  • 那既然信道可以阻塞當前的goroutine, 那么回到上一部分「goroutine」所遇到的問題「如何讓goroutine告訴主線我執行完畢了」 的問題來, 使用一個信道來告訴主線即可:
var complete chan int = make(chan int)func loop() {for i := 0; i < 10; i++ {fmt.Printf("%d ", i)}complete <- 0 // 執行完畢了,發個消息 }func main() {go loop()<- complete // 直到線程跑完, 取到消息. main在此阻塞住 }

如果不用信道來阻塞主線的話,主線就會過早跑完,loop線都沒有機會執行、、、

其實,無緩沖的信道永遠不會存儲數據,只負責數據的流通,為什么這么講呢?

  • 從無緩沖信道取數據,必須要有數據流進來才可以,否則當前線阻塞

  • 數據流入無緩沖信道, 如果沒有其他goroutine來拿走這個數據,那么當前線阻塞

所以,你可以測試下,無論如何,我們測試到的無緩沖信道的大小都是0 (len(channel))

如果信道正有數據在流動,我們還要加入數據,或者信道干澀,我們一直向無數據流入的空信道取數據呢? 就會引起死鎖

死鎖


一個死鎖的例子:

func main() {ch := make(chan int)<- ch // 阻塞main goroutine, 信道c被鎖 }

執行這個程序你會看到Go報這樣的錯誤:

fatal error: all goroutines are asleep - deadlock!

何謂死鎖? 操作系統有講過的,所有的線程或進程都在等待資源的釋放。如上的程序中, 只有一個goroutine, 所以當你向里面加數據或者存數據的話,都會鎖死信道, 并且阻塞當前 goroutine, 也就是所有的goroutine(其實就main線一個)都在等待信道的開放(沒人拿走數據信道是不會開放的),也就是死鎖咯。

我發現死鎖是一個很有意思的話題,這里有幾個死鎖的例子:

1.只在單一的goroutine里操作無緩沖信道,一定死鎖。比如你只在main函數里操作信道:

func main() {ch := make(chan int)ch <- 1 // 1流入信道,堵塞當前線, 沒人取走數據信道不會打開fmt.Println("This line code wont run") //在此行執行之前Go就會報死鎖 }

2.如下也是一個死鎖的例子:

var ch1 chan int = make(chan int) var ch2 chan int = make(chan int)func say(s string) {fmt.Println(s)ch1 <- <- ch2 // ch1 等待 ch2流出的數據 }func main() {go say("hello")<- ch1 // 堵塞主線 }

其中主線等ch1中的數據流出,ch1等ch2的數據流出,但是ch2等待數據流入,兩個goroutine都在等,也就是死鎖。

其實,總結來看,為什么會死鎖?非緩沖信道上如果發生了流入無流出,或者流出無流入,也就導致了死鎖。或者這樣理解 Go啟動的所有goroutine里的非緩沖信道一定要一個線里存數據,一個線里取數據,要成對才行 。所以下面的示例一定死鎖:

c, quit := make(chan int), make(chan int)go func() {c <- 1 // c通道的數據沒有被其他goroutine讀取走,堵塞當前goroutinequit <- 0 // quit始終沒有辦法寫入數據 }()<- quit // quit 等待數據的寫

仔細分析的話,是由于:主線等待quit信道的數據流出,quit等待數據寫入,而func被c通道堵塞,所有goroutine都在等,所以死鎖。

簡單來看的話,一共兩個線,func線中流入c通道的數據并沒有在main線中流出,肯定死鎖。

但是,是否果真 所有不成對向信道存取數據的情況都是死鎖?

如下是個反例:

func main() {c := make(chan int)go func() {c <- 1}() }

程序正常退出了,很簡單,并不是我們那個總結不起作用了,還是因為一個讓人很囧的原因,main又沒等待其它goroutine,自己先跑完了, 所以沒有數據流入c信道,一共執行了一個goroutine, 并且沒有發生阻塞,所以沒有死鎖錯誤。

那么死鎖的解決辦法呢?

最簡單的,把沒取走的數據取走,沒放入的數據放入, 因為無緩沖信道不能承載數據,那么就趕緊拿走!

具體來講,就死鎖例子3中的情況,可以這么避免死鎖:

c, quit := make(chan int), make(chan int)go func() {c <- 1quit <- 0 }()<- c // 取走c的數據! <-quit另一個解決辦法是緩沖信道, 即設置c有一個數據的緩沖大小:c := make(chan int, 1)

這樣的話,c可以緩存一個數據。也就是說,放入一個數據,c并不會掛起當前線, 再放一個才會掛起當前線直到第一個數據被其他goroutine取走, 也就是只阻塞在容量一定的時候,不達容量不阻塞。

這十分類似我們Python中的隊列Queue不是嗎?

無緩沖信道的數據進出順序


我們已經知道,無緩沖信道從不存儲數據,流入的數據必須要流出才可以。

觀察以下的程序:

var ch chan int = make(chan int)func foo(id int) { //id: 這個routine的標號ch <- id }func main() {// 開啟5個routinefor i := 0; i < 5; i++ {go foo(i)}// 取出信道中的數據for i := 0; i < 5; i++ {fmt.Print(<- ch)} }

我們開了5個goroutine,然后又依次取數據。其實整個的執行過程細分的話,5個線的數據 依次流過信道ch, main打印之, 而宏觀上我們看到的即 無緩沖信道的數據是先到先出,但是 無緩沖信道并不存儲數據,只負責數據的流通

緩沖信道


終于到了這個話題了, 其實緩存信道用英文來講更為達意: buffered channel.

緩沖這個詞意思是,緩沖信道不僅可以流通數據,還可以緩存數據。它是有容量的,存入一個數據的話 , 可以先放在信道里,不必阻塞當前線而等待該數據取走。

當緩沖信道達到滿的狀態的時候,就會表現出阻塞了,因為這時再也不能承載更多的數據了,「你們必須把 數據拿走,才可以流入數據」。

在聲明一個信道的時候,我們給make以第二個參數來指明它的容量(默認為0,即無緩沖):

var ch chan int = make(chan int, 2) // 寫入2個元素都不會阻塞當前goroutine, 存儲個數達到2的時候會阻塞

如下的例子,緩沖信道ch可以無緩沖的流入3個元素:

func main() {ch := make(chan int, 3)ch <- 1ch <- 2ch <- 3 }

如果你再試圖流入一個數據的話,信道ch會阻塞main線, 報死鎖。

也就是說,緩沖信道會在滿容量的時候加鎖。

其實,緩沖信道是先進先出的,我們可以把緩沖信道看作為一個線程安全的隊列:

func main() {ch := make(chan int, 3)ch <- 1ch <- 2ch <- 3fmt.Println(<-ch) // 1fmt.Println(<-ch) // 2fmt.Println(<-ch) // 3 }

信道數據讀取和信道關閉


你也許發現,上面的代碼一個一個地去讀取信道簡直太費事了,Go語言允許我們使用range來讀取信道:

func main() {ch := make(chan int, 3)ch <- 1ch <- 2ch <- 3for v := range ch {fmt.Println(v)} }

如果你執行了上面的代碼,會報死鎖錯誤的,原因是range不等到信道關閉是不會結束讀取的。也就是如果 緩沖信道干涸了,那么range就會阻塞當前goroutine, 所以死鎖咯。

那么,我們試著避免這種情況,比較容易想到的是讀到信道為空的時候就結束讀取:

ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 3 for v := range ch {fmt.Println(v)if len(ch) <= 0 { // 如果現有數據量為0,跳出循環break} }

以上的方法是可以正常輸出的,但是注意檢查信道大小的方法不能在信道存取都在發生的時候用于取出所有數據,這個例子 是因為我們只在ch中存了數據,現在一個一個往外取,信道大小是遞減的。

另一個方式是顯式地關閉信道:

ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 3// 顯式地關閉信道 close(ch)for v := range ch {fmt.Println(v) }

被關閉的信道會禁止數據流入, 是只讀的。我們仍然可以從關閉的信道中取出數據,但是不能再寫入數據了。

等待多gorountine的方案


那好,我們回到最初的一個問題,使用信道堵塞主線,等待開出去的所有goroutine跑完。

這是一個模型,開出很多小goroutine, 它們各自跑各自的,最后跑完了向主線報告。

我們討論如下2個版本的方案:

  • 只使用單個無緩沖信道阻塞主線

  • 使用容量為goroutines數量的緩沖信道

  • 對于方案1, 示例的代碼大概會是這個樣子:

    var quit chan int // 只開一個信道func foo(id int) {fmt.Println(id)quit <- 0 // ok, finished }func main() {count := 1000quit = make(chan int) // 無緩沖for i := 0; i < count; i++ {go foo(i)}for i := 0; i < count; i++ {<- quit} }

    對于方案2, 把信道換成緩沖1000的:

    quit = make(chan int, count) // 容量1000

    其實區別僅僅在于一個是緩沖的,一個是非緩沖的。

    對于這個場景而言,兩者都能完成任務, 都是可以的。

    • 無緩沖的信道是一批數據一個一個的「流進流出」

    • 緩沖信道則是一個一個存儲,然后一起流出去

    Go語言的并發和并行

    不知道你有沒有注意到一個現象,還是這段代碼,如果我跑在兩個goroutines里面的話:

    var quit chan int = make(chan int)func loop() {for i := 0; i < 10; i++ {fmt.Printf("%d ", i)}quit <- 0 }func main() {// 開兩個goroutine跑函數loop, loop函數負責打印10個數go loop()go loop()for i := 0; i < 2; i++ {<- quit} }

    我們觀察下輸出:

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

    這是不是有什么問題??

    以前我們用線程去做類似任務的時候,系統的線程會搶占式地輸出, 表現出來的是亂序地輸出。而goroutine為什么是這樣輸出的呢?

    goroutine是在并行嗎?


    我們找個例子測試下:

    package mainimport "fmt" import "time"var quit chan intfunc foo(id int) {fmt.Println(id)time.Sleep(time.Second) // 停頓一秒quit <- 0 // 發消息:我執行完啦! }func main() {count := 1000quit = make(chan int, count) // 緩沖1000個數據for i := 0; i < count; i++ { //開1000個goroutinego foo(i)}for i :=0 ; i < count; i++ { // 等待所有完成消息發送完畢。<- quit} }

    讓我們跑一下這個程序(之所以先編譯再運行,是為了讓程序跑的盡量快,測試結果更好):

    go build test.go time ./test ./test 0.01s user 0.01s system 1% cpu 1.016 total

    我們看到,總計用時接近一秒。 貌似并行了!

    我們看到,總計用時接近一秒。 貌似并行了!

    并行和并發


    從概念上講,并發和并行是不同的, 簡單來說看這個圖片

    • 兩個隊列,一個Coffee機器,那是并發
    • 兩個隊列,兩個Coffee機器,那是并行

    更多的資料: 并發不是并行, 當然Google上有更多關于并行和并發的區別。

    那么回到一開始的疑問上,從上面的兩個例子執行后的表現來看,多個goroutine跑loop函數會挨個goroutine去進行,而sleep則是一起執行的。

    這是為什么?

    默認地, Go所有的goroutines只能在一個線程里跑 。

    也就是說, 以上兩個代碼都不是并行的,但是都是是并發的。

    如果當前goroutine不發生阻塞,它是不會讓出CPU給其他goroutine的, 所以例子一中的輸出會是一個一個goroutine進行的,而sleep函數則阻塞掉了 當前goroutine, 當前goroutine主動讓其他goroutine執行, 所以形成了邏輯上的并行, 也就是并發。

    真正的并行


    為了達到真正的并行,我們需要告訴Go我們允許同時最多使用多個核。

    回到起初的例子,我們設置最大開2個原生線程, 我們需要用到runtime包(runtime包是goroutine的調度器):

    import ("fmt""runtime" )var quit chan int = make(chan int)func loop() {for i := 0; i < 100; i++ { //為了觀察,跑多些fmt.Printf("%d ", i)}quit <- 0 }func main() {runtime.GOMAXPROCS(2) // 最多使用2個核go loop()go loop()for i := 0; i < 2; i++ {<- quit} }

    這下會看到兩個goroutine會搶占式地輸出數據了。

    我們還可以這樣顯式地讓出CPU時間:

    func loop() {for i := 0; i < 10; i++ {runtime.Gosched() // 顯式地讓出CPU時間給其他goroutinefmt.Printf("%d ", i)}quit <- 0 }func main() {go loop()go loop()for i := 0; i < 2; i++ {<- quit} }

    觀察下結果會看到這樣有規律的輸出:

    0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

    其實,這種主動讓出CPU時間的方式仍然是在單核里跑。但手工地切換goroutine導致了看上去的“并行”。

    其實作為一個Python程序員,goroutine讓我更多地想到的是gevent的協程,而不是原生線程。

    關于runtime包對goroutine的調度,在stackoverflow上有一個不錯的答案:http://stackoverflow.com/questions/13107958/what-exactly-does-runtime-gosched-do

    一個小問題


    我在Segmentfault看到了這個問題:?http://segmentfault.com/q/1010000000207474

    題目說,如下的程序,按照理解應該打印下5次?"world"呀,可是為什么什么也沒有打印

    package mainimport ("fmt" )func say(s string) {for i := 0; i < 5; i++ {fmt.Println(s)} }func main() {go say("world") //開一個新的Goroutines執行for {} }

    樓下的答案已經很棒了,這里Go仍然在使用單核,for死循環占據了單核CPU所有的資源,而main線和say兩個goroutine都在一個線程里面, 所以say沒有機會執行。解決方案還是兩個:

    • 允許Go使用多核(runtime.GOMAXPROCS)
    • 手動顯式調動(runtime.Gosched)

    runtime調度器


    runtime調度器是個很神奇的東西,但是我真是但愿它不存在,我希望顯式調度能更為自然些,多核處理默認開啟。

    關于runtime包幾個函數:

    • Gosched?讓出cpu
    • NumCPU?返回當前系統的CPU核數量
    • GOMAXPROCS?設置最大的可同時使用的CPU核數
    • Goexit?退出當前goroutine(但是defer語句會照常執行)

    總結


    我們從例子中可以看到,默認的, 所有goroutine會在一個原生線程里跑,也就是只使用了一個CPU核。

    在同一個原生線程里,如果當前goroutine不發生阻塞,它是不會讓出CPU時間給其他同線程的goroutines的,這是Go運行時對goroutine的調度,我們也可以使用runtime包來手工調度。

    本文開頭的兩個例子都是限制在單核CPU里執行的,所有的goroutines跑在一個線程里面,分析如下:

    • 對于代碼例子一(loop函數的那個),每個goroutine沒有發生堵塞(直到quit流入數據), 所以在quit之前每個goroutine不會主動讓出CPU,也就發生了串行打印
    • 對于代碼例子二(time的那個),每個goroutine在sleep被調用的時候會阻塞,讓出CPU, 所以例子二并發執行。

    那么關于我們開啟多核的時候呢?Go語言對goroutine的調度行為又是怎么樣的?

    我們可以在Golang官方網站的這里 找到一句話:

    When a coroutine blocks, such as by calling a blocking system call, the run-time automatically moves other coroutines on the same operating system thread to a different, runnable thread so they won’t be blocked.

    也就是說:

    當一個goroutine發生阻塞,Go會自動地把與該goroutine處于同一系統線程的其他goroutines轉移到另一個系統線程上去,以使這些goroutines不阻塞

    開啟多核的實驗


    仍然需要做一個實驗,來測試下多核支持下goroutines的對原生線程的分配, 也驗證下我們所得到的結論“goroutine不阻塞不放開CPU”。

    實驗代碼如下:

    package mainimport ("fmt""runtime" )var quit chan int = make(chan int)func loop(id int) { // id: 該goroutine的標號for i := 0; i < 10; i++ { //打印10次該goroutine的標號fmt.Printf("%d ", id)}quit <- 0 }func main() {runtime.GOMAXPROCS(2) // 最多同時使用2個核for i := 0; i < 3; i++ { //開三個goroutinego loop(i)}for i := 0; i < 3; i++ {<- quit} }

    多跑幾次會看到類似這些輸出(不同機器環境不一樣):

    0 0 0 0 0 1 1 0 0 1 0 0 1 0 1 2 1 2 1 2 1 2 1 2 1 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 1 2 1 2 1 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 2 0 2 0 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 1 0 0 1 0 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 2 2

    執行它我們會發現以下現象:

    • 有時會發生搶占式輸出(說明Go開了不止一個原生線程,達到了真正的并行)
    • 有時會順序輸出, 打印完0再打印1, 再打印2(說明Go開一個原生線程,單線程上的goroutine不阻塞不松開CPU)

    那么,我們還會觀察到一個現象,無論是搶占地輸出還是順序的輸出,都會有那么兩個數字表現出這樣的現象:

    • 一個數字的所有輸出都會在另一個數字的所有輸出之前

    原因是, 3個goroutine分配到至多2個線程上,就會至少兩個goroutine分配到同一個線程里,單線程里的goroutine 不阻塞不放開CPU, 也就發生了順序輸出。

    Go語言并發的設計模式和應用場景

    以下設計模式和應用場景來自Google IO上的關于Goroutine的PPT:https://talks.golang.org/2012/concurrency.slide

    本文的示例代碼在:?https://github.com/hit9/Go-patterns-with-channel

    生成器


    在Python中我們可以使用yield關鍵字來讓一個函數成為生成器,在Go中我們可以使用信道來制造生成器(一種lazy load類似的東西)。

    當然我們的信道并不是簡單的做阻塞主線的功能來使用的哦。

    下面是一個制作自增整數生成器的例子,直到主線向信道索要數據,我們才添加數據到信道

    func xrange() chan int{ // xrange用來生成自增的整數var ch chan int = make(chan int)go func() { // 開出一個goroutinefor i := 0; ; i++ {ch <- i // 直到信道索要數據,才把i添加進信道}}()return ch }func main() {generator := xrange()for i:=0; i < 1000; i++ { // 我們生成1000個自增的整數!fmt.Println(<-generator)} }

    這不禁叫我想起了Python中可愛的xrange, 所以給了生成器這個名字!

    服務化


    比如我們加載一個網站的時候,例如我們登入新浪微博,我們的消息數據應該來自一個獨立的服務,這個服務只負責 返回某個用戶的新的消息提醒。

    如下是一個使用示例:

    func get_notification(user string) chan string{/** 此處可以查詢數據庫獲取新消息等等..*/notifications := make(chan string)go func() { // 懸掛一個信道出去notifications <- fmt.Sprintf("Hi %s, welcome to weibo.com!", user)}()return notifications }func main() {jack := get_notification("jack") // 獲取jack的消息joe := get_notification("joe") // 獲取joe的消息// 獲取消息的返回fmt.Println(<-jack)fmt.Println(<-joe) }

    多路復合


    上面的例子都使用一個信道作為返回值,可以把信道的數據合并到一個信道的。 不過這樣的話,我們需要按順序輸出我們的返回值(先進先出)。

    如下,我們假設要計算很復雜的一個運算 100-x , 分為三路計算, 最后統一在一個信道中取出結果:

    func do_stuff(x int) int { // 一個比較耗時的事情,比如計算time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) //模擬計算return 100 - x // 假如100-x是一個很費時的計算 }func branch(x int) chan int{ // 每個分支開出一個goroutine做計算并把計算結果流入各自信道ch := make(chan int)go func() {ch <- do_stuff(x)}()return ch }func fanIn(chs... chan int) chan int {ch := make(chan int)for _, c := range chs {// 注意此處明確傳值go func(c chan int) {ch <- <- c}(c) // 復合}return ch }func main() {result := fanIn(branch(1), branch(2), branch(3))for i := 0; i < 3; i++ {fmt.Println(<-result)} }

    select監聽信道


    Go有一個語句叫做select,用于監測各個信道的數據流動。

    如下的程序是select的一個使用例子,我們監視三個信道的數據流出并收集數據到一個信道中。有了select, 我們在 多路復合中的示例代碼中的函數fanIn還可以這么來寫(這樣就不用開好幾個goroutine來取數據了):

    func fanIn(branches ... chan int) chan int {c := make(chan int)go func() {for i := 0 ; i < len(branches); i++ { //select會嘗試著依次取出各個信道的數據select {case v1 := <- branches[i]: c <- v1}}}()return c }

    用select的時候,有時需要超時處理, 其中的timeout信道相當有趣:結束標志


    在Go并發與并行筆記一我們已經講過信道的一個很重要也很平常的應用,就是使用無緩沖信道來阻塞主線,等待goroutine結束。

    這樣我們不必再使用timeout。

    那么對上面的timeout來結束主線的方案作個更新:菊花鏈


    簡單地來說,數據從一端流入,從另一端流出,看上去好像一個鏈表,不知道為什么要取這么個尷尬的名字。。

    菊花鏈的英文名字叫做: Daisy-chain, 它的一個應用就是做過濾器,比如我們來篩下100以內的素數(你需要先知道什么是篩法)

    程序有詳細的注釋,不再說明了。隨機數生成器


    信道可以做生成器使用,作為一個特殊的例子,它還可以用作隨機數生成器。如下是一個隨機01生成器:定時器


    我們剛才其實已經接觸了信道作為定時器, time包里的After會制作一個定時器。

    看看我們的定時器吧!

    /** 利用信道做定時器*/package mainimport ("fmt""time" )func timer(duration time.Duration) chan bool {ch := make(chan bool)go func() {time.Sleep(duration)ch <- true // 到時間啦!}()return ch }func main() {timeout := timer(time.Second) // 定時1sfor {select {case <- timeout:fmt.Println("already 1s!") // 到時間return //結束程序}} }

    TODO


    Google的應用場景例子。

    本篇主要總結了使用信道, goroutine的一些設計模式。

    ?

    間接轉載地址:?https://blog.csdn.net/sb___itfk/article/details/79045906

    總結

    以上是生活随笔為你收集整理的Go语言并发与并行的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 快射视频网| 大波大乳videos巨大 | 色播五月激情 | 又黄又刺激的视频 | 久视频在线 | 爱视频福利网 | 男ji大巴进入女人的视频 | 精品人妻无码一区二区三 | 亚洲h视频在线观看 | 性感美女高潮 | 极品美女扒开粉嫩小泬 | 日韩一卡 | 色中色综合| 免费禁漫天堂a3d | 91theporn国产在线观看 | 精品国产乱码久久久久久免费 | av三级| 黄网站免费大全入口 | 欧美美女色图 | 熟女毛毛多熟妇人妻aⅴ在线毛片 | 欧美成人视 | 丰满少妇乱子伦精品看片 | 亚洲人女屁股眼交6 | 亚洲小说春色综合另类 | 国产午夜精品理论片在线 | 国产激情图片 | 虫族全黄h全肉污文 | 97精品在线视频 | 免费观看一级一片 | 综合久久激情 | 日韩黄色影院 | 国产夫妻av| 亚洲精品日韩精品 | 黄色免费网站 | 网站在线播放 | h片在线观看免费 | 草草影院欧美 | 亚洲男性天堂 | 国产精品久久久一区二区三区 | 久久爱网 | 久久久免费 | 久久久国 | 精品人妻一区二区三区四区不卡 | 无码人妻精品一区二区50 | 亚洲综合五月天婷婷丁香 | 日本亚洲欧美 | 成人黄色免费在线观看 | 香蕉网在线视频 | 91久久久久 | 日韩不卡一二三区 | 久久精品网 | 精品毛片一区二区三区 | 很黄的性视频 | 久久久视频在线观看 | www成人免费视频 | 在线视频日韩精品 | 亚洲第一欧美 | 男人的天堂视频在线观看 | 中国美女一级看片 | 中日韩黄色大片 | 欧美一区二区在线免费观看 | 久久影视一区二区 | 天堂资源中文在线 | 美女午夜激情 | 精品国产精品网麻豆系列 | 亚洲精品乱码久久久久 | 91pron在线 | 污污的视频软件 | 国产精品伦一区二区三级视频 | 91免费视频观看 | 亚洲色偷偷色噜噜狠狠99网 | 黄站在线观看 | 国产亚洲色婷婷久久99精品91 | 中文字幕av网址 | 欧美操穴| 秋霞国产 | 久久4| 欧洲黄色录像 | 亚洲午夜视频在线 | 女人久久 | 日韩在线视频网 | 国产一区免费 | 五月天丁香在线 | av狠狠操 | 日韩综合久久 | 午夜福利123 | 日日夜夜噜 | 国产精品一区二区自拍 | 色在线播放 | 亚洲一区二区三区久久 | 久久成人免费电影 | 欧美视频一二三区 | 色窝av | 成人综合影院 | 免费成人在线视频观看 | 亚洲av成人片无码 | 久久精品久久久久久 | 狠狠操一区二区 | 性xxxx欧美|