Go 并發(fā)可視化解釋 — 通道

當(dāng)談到并發(fā)時(shí)删窒,許多編程語言采用共享內(nèi)存/狀態(tài)模型。然而顺囊,Go 通過實(shí)現(xiàn)通信順序進(jìn)程 (CSP) 來區(qū)別自己。在CSP中蕉拢,一個(gè)程序由并行進(jìn)程組成特碳,這些進(jìn)程不共享狀態(tài);相反晕换,它們使用通道來通信并同步它們的行為午乓。

因此,對于有興趣采用 Go 的開發(fā)者來說闸准,理解通道的工作原理變得至關(guān)重要益愈。在這篇文章中,我將使用 Gophers 運(yùn)行他們的想象咖啡店的有趣類比來描述通道夷家,因?yàn)槲覉?jiān)信人們更容易通過視覺來學(xué)習(xí)蒸其。

場景

Partier、Candier 和 Stringer 正在經(jīng)營一個(gè)咖啡館库快。由于制作咖啡所需的時(shí)間比接受訂單要長摸袁,Partier 會(huì)協(xié)助接受顧客的訂單,然后將這些訂單傳遞給廚房义屏,Candier 和 Stringer 在那里準(zhǔn)備咖啡靠汁。

無緩沖通道

最初,咖啡店以最簡單的方式運(yùn)作:每當(dāng)收到一個(gè)新訂單時(shí)闽铐,Partier 將訂單放入通道蝶怔,并等待 Candier 或 Stringer 拿走它,然后再接受任何新訂單兄墅。通過無緩沖通道踢星,使用 ch := make(chan Order) 創(chuàng)建,實(shí)現(xiàn)了 Partier 和廚房之間的這種溝通隙咸。當(dāng)通道中沒有掛起的訂單時(shí)斩狱,即使 Stringer 和 Candier 都準(zhǔn)備好接受新訂單耳高,他們?nèi)匀惶幱诘却隣顟B(tài),等待新訂單到來所踊。

當(dāng)收到一個(gè)新訂單時(shí)泌枪,Partier 將其放入通道,使該訂單可以被 Candier 或 Stringer 拿走秕岛。但在接受新訂單之前碌燕,Partier 必須等待其中一個(gè)人從通道中取出訂單。

當(dāng) Stringer 和 Candier 都準(zhǔn)備好接受新訂單時(shí)继薛,新訂單將立即被其中一個(gè)拿走修壕。然而,不能保證或預(yù)測到底是誰會(huì)拿到訂單遏考。Stringer 和 Candier 之間的選擇是不確定的慈鸠,這取決于諸如調(diào)度和 Go 運(yùn)行時(shí)的內(nèi)部機(jī)制之類的因素。假設(shè) Candier 得到了這個(gè)第一個(gè)訂單灌具。

在 Candier 完成處理第一個(gè)訂單后青团,她回到等待狀態(tài)。如果沒有新的訂單到來咖楣,兩個(gè)工人督笆,Candier 和 Stringer,都會(huì)閑置诱贿,直到 Partier 將另一個(gè)訂單放入通道供他們處理娃肿。

當(dāng)收到一個(gè)新訂單,且 Stringer 和 Candier 都可以處理它時(shí)珠十。即使 Candier 剛剛處理了上一個(gè)訂單料扰,接收新訂單的特定工人仍然是不確定的。在這個(gè)場景中焙蹭,我們假設(shè) Candier 再次被分配了這第二個(gè)訂單记罚。

新的訂單 order3 到來,由于 Candier 正在處理 order2壳嚎,她并沒有等在 order := <-ch 這一行桐智,Stringer 成為了唯一可以接收 order3 的可用工人。因此烟馅,他會(huì)得到它说庭。

order3 發(fā)送給 Stringer 之后,order4 立即到達(dá)郑趁。此時(shí)刊驴,Stringer 和 Candier 都已經(jīng)在處理他們各自的訂單,沒有人可以拿走 order4。因?yàn)橥ǖ罌]有緩沖捆憎,將 order4 放入通道會(huì)阻塞 Partier舅柜,直到 Stringer 或 Candier 有一個(gè)變得可以接受 order4。我經(jīng)扯愣瑁看到人們對無緩沖通道(用 make(chan order)make(chan order, 0) 創(chuàng)建)和緩沖大小為1的通道(用 make(chan order, 1) 創(chuàng)建)感到困惑致份。因此,他們錯(cuò)誤地期望 ch <- order4 立即完成础拨,允許 Partier 接受 order5氮块,然后在 ch <- order5 上被阻塞。如果你也有這種想法诡宗,我在 Go Playground 上創(chuàng)建了一個(gè)代碼片段滔蝉,幫助你糾正誤解 https://go.dev/play/p/shRNiDDJYB4

緩沖通道

無緩沖通道確實(shí)可以工作塔沃,但它限制了整體的吞吐量蝠引。如果他們僅接受一定數(shù)量的訂單在后臺(tái)(廚房)順序處理會(huì)更好。這可以通過緩沖通道來實(shí)現(xiàn)≈瘢現(xiàn)在螃概,即使 Stringer 和 Candier 忙于處理他們的訂單,只要通道沒有滿名扛,Partier 仍然可以在通道中留下新的訂單并繼續(xù)接受其他訂單,例如茧痒,最多3個(gè)掛起的訂單肮韧。

通過引入緩沖通道,咖啡店增強(qiáng)了處理更多訂單的能力旺订。但是弄企,選擇適當(dāng)?shù)木彌_大小以保持合理的客戶等待時(shí)間是至關(guān)重要的。畢竟区拳,沒有客戶愿意忍受過長的等待時(shí)間拘领。有時(shí),拒絕新訂單可能比接受它們并且無法及時(shí)完成它們更為可取樱调。此外约素,在短暫的容器化(Docker)應(yīng)用程序中使用緩沖通道時(shí)必須小心,因?yàn)轭A(yù)期會(huì)有隨機(jī)重啟笆凌,在這種情況下圣猎,從通道中恢復(fù)消息可能是一項(xiàng)具有挑戰(zhàn)性的任務(wù),甚至可能是不可能的乞而。

通道與阻塞隊(duì)列

盡管它們在本質(zhì)上是不同的送悔,但Java中的Blocking Queue是用來在線程之間通信的,而Go中的Channel是用于Goroutine的通信,BlockingQueue 和 Channel 的行為在某種程度上是相似的欠啤。如果你熟悉BlockingQueue荚藻,那么理解Channel肯定會(huì)很容易。

常見用途

通道是Go應(yīng)用中的一個(gè)基礎(chǔ)且廣泛使用的功能洁段,服務(wù)于各種目的应狱。通道的一些常見用途包括:

  • Goroutine 通信:通道使不同的goroutines之間能夠進(jìn)行消息交換,使它們可以協(xié)作而無需直接共享狀態(tài)眉撵。
  • 工作池:正如上面的示例中所見侦香,通道經(jīng)常用于管理工作池,其中多個(gè)相同的工作人員從共享通道處理傳入的任務(wù)纽疟。
  • 扇出罐韩,扇入:通道促進(jìn)了扇出,扇入模式污朽,其中多個(gè)goroutines(扇出)執(zhí)行工作并將結(jié)果發(fā)送到一個(gè)通道散吵,而另一個(gè)goroutine(扇入)消費(fèi)這些結(jié)果。
  • 超時(shí)和截止日期:與 select 語句結(jié)合使用蟆肆,通道可以用于處理超時(shí)和截止日期矾睦,確保程序可以優(yōu)雅地處理延遲并避免無限的等待。

我將在其他文章中更詳細(xì)地探討通道的不同用途炎功。但是枚冗,現(xiàn)在,讓我們通過實(shí)現(xiàn)上述的咖啡店場景來結(jié)束這篇入門博客蛇损,并親眼看到通道如何在實(shí)踐中工作赁温。我們將探索Partier、Candier和Stringer之間的互動(dòng)淤齐,并觀察通道如何促進(jìn)他們之間的順暢溝通和協(xié)調(diào)股囊,使咖啡店能夠有效地處理訂單和同步。

給我看你的代碼更啄!

package main

import (
    "fmt"
    "log"
    "math/rand"
    "sync"
    "time"
)

func main() {
    ch := make(chan order, 3)

    wg := &sync.WaitGroup{} // More on WaitGroup another day
    wg.Add(2)

    go func() {
        defer wg.Done()
        worker("Candier", ch)
    }()

    go func() {
        defer wg.Done()
        worker("Stringer", ch)
    }()

    for i := 0; i < 10; i++ {
        waitForOrders()
        o := order(i)
        log.Printf("Partier: I %v, I will pass it to the channel\n", o)
        ch <- o
    }

    log.Println("No more orders, closing the channel to signify workers to stop")
    close(ch)

    log.Println("Wait for workers to gracefully stop")
    wg.Wait()

    log.Println("All done")
}

func waitForOrders() {
    processingTime := time.Duration(rand.Intn(2)) * time.Second
    time.Sleep(processingTime)
}

func worker(name string, ch <-chan order) {
    for o := range ch {
        log.Printf("%s: I got %v, I will process it\n", name, o)
        processOrder(o)
        log.Printf("%s: I completed %v, I'm ready to take a new order\n", name, o)
    }
    log.Printf("%s: I'm done\n", name)
}

func processOrder(_ order) {
    processingTime := time.Duration(2+rand.Intn(2)) * time.Second
    time.Sleep(processingTime)
}

type order int

func (o order) String() string {
    return fmt.Sprintf("order-%02d", o)
}

您可以復(fù)制這段代碼稚疹,在您的IDE上進(jìn)行調(diào)整并運(yùn)行它,以更好地理解通道是如何工作的祭务。

如果您對保持對軟件工程領(lǐng)域的最新動(dòng)態(tài)感興趣内狗,請關(guān)注我。我將確保讓您了解最新信息义锥!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末其屏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子缨该,更是在濱河造成了極大的恐慌偎行,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛤袒,居然都是意外死亡熄云,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門妙真,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缴允,“玉大人,你說我怎么就攤上這事珍德×钒悖” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵锈候,是天一觀的道長薄料。 經(jīng)常有香客問我,道長泵琳,這世上最難降的妖魔是什么摄职? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮获列,結(jié)果婚禮上谷市,老公的妹妹穿的比我還像新娘。我一直安慰自己击孩,他們只是感情好迫悠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巩梢,像睡著了一般创泄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上且改,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天验烧,我揣著相機(jī)與錄音板驳,去河邊找鬼又跛。 笑死,一個(gè)胖子當(dāng)著我的面吹牛若治,可吹牛的內(nèi)容都是我干的慨蓝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼端幼,長吁一口氣:“原來是場噩夢啊……” “哼礼烈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婆跑,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤此熬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體犀忱,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡募谎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阴汇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片数冬。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搀庶,靈堂內(nèi)的尸體忽然破棺而出拐纱,到底是詐尸還是另有隱情,我是刑警寧澤哥倔,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布秸架,位于F島的核電站,受9級(jí)特大地震影響未斑,放射性物質(zhì)發(fā)生泄漏咕宿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一蜡秽、第九天 我趴在偏房一處隱蔽的房頂上張望府阀。 院中可真熱鬧,春花似錦芽突、人聲如沸试浙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽田巴。三九已至,卻和暖如春挟秤,著一層夾襖步出監(jiān)牢的瞬間壹哺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工艘刚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留管宵,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓攀甚,卻偏偏與公主長得像箩朴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子秋度,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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