Golang-使用帶緩沖的Channel控制并發(fā)

導讀

??????Channel是Golang實現(xiàn)并發(fā)編程非常重要的組成部分,Channel是一種內(nèi)建的核心數(shù)據(jù)類型卿操,需要使用make函數(shù)初始化警检,包括無緩沖的Channel(unbuffered Channel)有緩沖的Channel(buffered Channel)兩種孙援。無緩沖的Channel(unbuffered Channel) 主要用于goroutine之間的同步,有緩沖的Channel(buffered Channel)主要用于異步通信扇雕、控制goroutine并發(fā)數(shù)量拓售。

Unbuffered := make(chan int) // Unbuffered channel of integer type
buffered := make(chan int, 10)  // Buffered channel of integer type

場景

??????在我們的日常開發(fā)工作中,時常有需要控制并發(fā)的場景镶奉,如控制訪問一個接口的并發(fā)础淤,運維系統(tǒng)初始化機器服務(wù)的數(shù)量等。下面介紹一下如何使用有緩沖的Channel(buffered Channel)實現(xiàn)控制并發(fā)數(shù)量腮鞍。

package main

import (
    "flag"
    "fmt"
    "time"
)

// Fake a long and difficult work.
func DoWork() {
    time.Sleep(5000 * time.Millisecond)
}

func main() {
    maxNbConcurrentGoroutines := flag.Int("maxNbConcurrentGoroutines", 5, "the number of goroutines that are allowed to run concurrently")
    nbJobs := flag.Int("nbJobs", 100, "the number of jobs that we need to do")
    flag.Parse()

    // Dummy channel to coordinate the number of concurrent goroutines.
    // This channel should be buffered otherwise we will be immediately blocked
    // when trying to fill it.
    concurrentGoroutines := make(chan struct{}, *maxNbConcurrentGoroutines)
    // The done channel indicates when a single goroutine has
    // finished its job.
    done := make(chan bool)
    // The waitForAllJobs channel allows the main program
    // to wait until we have indeed done all the jobs.
    waitForAllJobs := make(chan bool)

    // Collect all the jobs, and since the job is finished, we can
    // release another spot for a goroutine.
    go func() {
        for i := 0; i < *nbJobs; i++ {
            <-done
        }
        // We have collected all the jobs, the program
        // can now terminate
        waitForAllJobs <- true
    }()

    // Try to start nbJobs jobs
    for i := 1; i <= *nbJobs; i++ {
        fmt.Printf("ID: %v: waiting to launch!\n", i)
        // Try to receive from the concurrentGoroutines channel. When we have something,
        // it means we can start a new goroutine because another one finished.
        // Otherwise, it will block the execution until an execution
        // spot is available.
        concurrentGoroutines <- struct{}{}

        fmt.Printf("ID: %v: it's my turn!\n", i)
        go func(id int) {
            DoWork()
            fmt.Printf("ID: %v: all done!\n", id)
            done <- true
            <-concurrentGoroutines
        }(i)
    }

    // Wait for all jobs to finish
    <-waitForAllJobs
}

這里代碼源自Github值骇,我對其做了一些改動莹菱,讓執(zhí)行流程更容易理解移国、輸出結(jié)果更加明顯,下面解釋一下這段程序:

  1. 使用flag包創(chuàng)建命令行參數(shù)道伟,可以自己指定并發(fā)的數(shù)量maxNbConcurrentGoroutines和總共任務(wù)數(shù)量nbJobs
maxNbConcurrentGoroutines := flag.Int("maxNbConcurrentGoroutines", 5, "the number of goroutines that are allowed to run concurrently")
nbJobs := flag.Int("nbJobs", 100, "the number of jobs that we need to do")
flag.Parse()

運行一個并發(fā)為10迹缀,總數(shù)為100的示例:

go run limit_concurrency.go -maxNbConcurrentGoroutines 10 -nbJobs 100
  1. 創(chuàng)建concurrentGoroutines作為控制并發(fā)的帶緩沖的Channel, done用于記錄一個goroutine完成,waitForAllJobs用于阻塞main函數(shù)蜜徽,等待所有g(shù)oroutine完成祝懂。
concurrentGoroutines := make(chan struct{}, *maxNbConcurrentGoroutines)
done := make(chan bool)
waitForAllJobs := make(chan bool)
  1. 創(chuàng)建一個收集所有g(shù)oroutine運行狀態(tài)的goroutine,當所有g(shù)oroutine完成后向waitForAllJobs發(fā)送“完成信號”拘鞋。
    go func() {
        for i := 0; i < *nbJobs; i++ {
            <-done
        }
        waitForAllJobs <- true
    }()
  1. 這里我們叫它任務(wù)創(chuàng)建著砚蓬,或者叫生成者也可以,首先會向concurrentGoroutines寫入空的struct盆色,因為concurrentGoroutines的buffer是10灰蛙,所以這里不會阻塞,直到for循環(huán)執(zhí)行10次隔躲,將buffer填滿摩梧,同時的也創(chuàng)建了10個goroutine用于執(zhí)行我們的任務(wù),當任務(wù)執(zhí)行完畢(這里都暫定執(zhí)行5s)宣旱,向done發(fā)送true通知第三步的goroutine我執(zhí)行完了仅父,接著讀取concurrentGoroutines,“釋放”一個空間浑吟,讓其他goroutine可以進來繼續(xù)執(zhí)行笙纤,但是怎么都不會超出buffer的個數(shù),等所有任務(wù)執(zhí)行完组力,waitForAllJobs收到了第三步gorotine發(fā)送的信號省容,整個程序結(jié)束,這就實現(xiàn)了控制并發(fā)忿项。
    for i := 1; i <= *nbJobs; i++ {
        fmt.Printf("ID: %v: waiting to launch!\n", i)
        concurrentGoroutines <- struct{}{}
        fmt.Printf("ID: %v: it's my turn!\n", i)
        go func(id int) {
            DoWork()
            fmt.Printf("ID: %v: all done!\n", id)
            done <- true
            <-concurrentGoroutines
        }(i)
    }
    <-waitForAllJobs

好了蓉冈,小伙伴們城舞,快運行下看看結(jié)果吧,Let's ready to Go :)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寞酿,一起剝皮案震驚了整個濱河市家夺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伐弹,老刑警劉巖拉馋,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惨好,居然都是意外死亡煌茴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門日川,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔓腐,“玉大人,你說我怎么就攤上這事龄句』芈郏” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵分歇,是天一觀的道長傀蓉。 經(jīng)常有香客問我,道長职抡,這世上最難降的妖魔是什么葬燎? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮缚甩,結(jié)果婚禮上谱净,老公的妹妹穿的比我還像新娘。我一直安慰自己蹄胰,他們只是感情好岳遥,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裕寨,像睡著了一般浩蓉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宾袜,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天捻艳,我揣著相機與錄音,去河邊找鬼庆猫。 笑死认轨,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的月培。 我是一名探鬼主播嘁字,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼恩急,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纪蜒?” 一聲冷哼從身側(cè)響起衷恭,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纯续,沒想到半個月后随珠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡猬错,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年窗看,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倦炒。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡显沈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出析校,到底是詐尸還是另有隱情构罗,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布智玻,位于F島的核電站,受9級特大地震影響芙代,放射性物質(zhì)發(fā)生泄漏吊奢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一纹烹、第九天 我趴在偏房一處隱蔽的房頂上張望页滚。 院中可真熱鬧,春花似錦铺呵、人聲如沸裹驰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幻林。三九已至,卻和暖如春音念,著一層夾襖步出監(jiān)牢的瞬間沪饺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工闷愤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留整葡,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓讥脐,卻偏偏與公主長得像遭居,于是被迫代替她去往敵國和親啼器。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容