国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Go并發(fā)中for range與Channel的正確用法,你真的清楚嗎?

開發(fā) 前端
記住Go并發(fā)設(shè)計(jì)的黃金法則:通過(guò)通信共享內(nèi)存,而不是通過(guò)共享內(nèi)存進(jìn)行通信。優(yōu)先使用Channel來(lái)組織數(shù)據(jù)流,只有在確實(shí)需要時(shí)才使用鎖。這樣寫出的代碼不僅更安全,也更具有Go語(yǔ)言的特色。

訓(xùn)練營(yíng)里有位學(xué)員提出了一個(gè)很典型的問(wèn)題:

“goroutine和Channel我都理解了,但為什么有些例子要加鎖,有些又不用?for range在Channel里到底起什么作用?”

這個(gè)問(wèn)題問(wèn)到了Go并發(fā)編程的核心,今天我們就來(lái)徹底講清楚。

理解困惑的根源

學(xué)員的困惑主要集中在兩點(diǎn):

  1. 雖然知道有緩沖和無(wú)緩沖Channel的區(qū)別,但看到for range與Channel結(jié)合使用時(shí)就感到困惑,更不明白為什么求和場(chǎng)景還需要加鎖
  2. for range在切片和Channel中的行為完全不同,這個(gè)語(yǔ)法糖的實(shí)際價(jià)值是什么

鎖的使用時(shí)機(jī):通過(guò)兩個(gè)場(chǎng)景徹底理解

場(chǎng)景一:搶購(gòu)火車票——不加鎖必然導(dǎo)致超賣

假設(shè)有100張車票,1000個(gè)用戶同時(shí)搶購(gòu)。核心邏輯很簡(jiǎn)單:

ticketCount := 100

// 1000個(gè)goroutine同時(shí)執(zhí)行:
if ticketCount > 0 {
    ticketCount--  // 不加鎖會(huì)導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)
}

問(wèn)題所在:檢查票數(shù)和減少票數(shù)是兩個(gè)獨(dú)立操作,中間可能被其他goroutine打斷。goroutine A看到還剩1張票,正準(zhǔn)備扣減時(shí),goroutine B也看到了這1張票,結(jié)果兩人都購(gòu)買成功,票數(shù)變?yōu)?1。鎖的作用就是確保這兩個(gè)操作成為一個(gè)原子性的整體,同一時(shí)間只允許一個(gè)goroutine執(zhí)行。

場(chǎng)景二:并行求和——看似簡(jiǎn)單,實(shí)則暗藏?cái)?shù)據(jù)競(jìng)爭(zhēng)

sum := 0
for _, num := range numbers {
    go func(n int) {
        sum += n  // 不加鎖,結(jié)果不可預(yù)測(cè)
    }(num)
}

問(wèn)題所在:雖然這不是資源扣減,但sum += n實(shí)際上包含三個(gè)步驟:讀取sum值 → 執(zhí)行加法 → 寫回結(jié)果。兩個(gè)goroutine可能同時(shí)讀取到100,各自加5后都寫回105,而正確結(jié)果應(yīng)該是110。這就是數(shù)據(jù)競(jìng)爭(zhēng)——不是資源不足,而是更新操作被覆蓋了

更地道的Go風(fēng)格:用Channel替代鎖

Go語(yǔ)言的哲學(xué)是“不要通過(guò)共享內(nèi)存來(lái)通信,而要通過(guò)通信來(lái)共享內(nèi)存”。我們可以用Channel重構(gòu)上面的求和示例:

func sumWithChannel(numbers []int) int {
    ch := make(chanint)
    var wg sync.WaitGroup
    
    // 啟動(dòng)所有計(jì)算goroutine
    for _, num := range numbers {
        wg.Add(1)
        gofunc(n int) {
            defer wg.Done()
            ch <- n  // 每個(gè)goroutine只發(fā)送自己的結(jié)果
        }(num)
    }
    
    // 啟動(dòng)一個(gè)goroutine在完成后關(guān)閉channel
    gofunc() {
        wg.Wait()
        close(ch)  // 所有發(fā)送完成后安全關(guān)閉通道
    }()
    
    // 主goroutine負(fù)責(zé)收集結(jié)果
    sum := 0
    for n := range ch {  // 自動(dòng)循環(huán)直到channel關(guān)閉
        sum += n
    }
    return sum
}

關(guān)鍵改進(jìn)

  1. 使用WaitGroup確保所有g(shù)oroutine完成工作
  2. 單獨(dú)的goroutine負(fù)責(zé)關(guān)閉channel,避免過(guò)早關(guān)閉
  3. 使用for n := range ch自動(dòng)讀取直到channel關(guān)閉,代碼更簡(jiǎn)潔

設(shè)計(jì)優(yōu)勢(shì):每個(gè)goroutine只處理自己的數(shù)據(jù),通過(guò)Channel將結(jié)果發(fā)送到統(tǒng)一收集點(diǎn),完全避免了數(shù)據(jù)競(jìng)爭(zhēng),代碼也更清晰。

必須使用鎖的三種情況

盡管Channel是推薦的方式,但某些場(chǎng)景下鎖仍然是必要選擇:

  • 并發(fā)讀寫同一變量:當(dāng)有g(shù)oroutine在寫入時(shí),其他goroutine需要讀取或?qū)懭朐撟兞?/span>
  • 檢查后執(zhí)行:像搶票場(chǎng)景,需要先檢查條件再執(zhí)行操作,這兩步必須原子化
  • 多步驟事務(wù)操作:如銀行轉(zhuǎn)賬需要同時(shí)完成扣款和存款,必須保證原子性

可以避免鎖的替代方案

  • 獨(dú)立計(jì)算+結(jié)果合并:每個(gè)goroutine計(jì)算獨(dú)立部分,通過(guò)Channel傳遞結(jié)果
  • 數(shù)據(jù)分片處理:將大數(shù)據(jù)集分割,每個(gè)goroutine處理一個(gè)子集
  • 只讀共享數(shù)據(jù):所有g(shù)oroutine只讀取不修改共享數(shù)據(jù)
  • 使用sync/atomic:對(duì)簡(jiǎn)單的數(shù)值操作使用原子操作

完整代碼對(duì)比:三種實(shí)現(xiàn)方式

package main

import (
    "fmt"
    "sync"
)

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // 方案一:使用鎖(傳統(tǒng)共享內(nèi)存方式)
    var mu sync.Mutex
    sum1 := 0
    var wg1 sync.WaitGroup
    
    for _, n := range numbers {
        wg1.Add(1)
        gofunc(x int) {
            defer wg1.Done()
            mu.Lock()
            sum1 += x
            mu.Unlock()
        }(n)
    }
    wg1.Wait()
    fmt.Println("加鎖求和:", sum1)  // 輸出: 55
    
    // 方案二:使用Channel(推薦方式)
    sum2 := sumWithChannel(numbers)
    fmt.Println("Channel求和:", sum2)  // 輸出: 55
    
    // 方案三:Worker Pool模式(處理大量數(shù)據(jù)時(shí)更高效)
    sum3 := sumWithWorkerPool(numbers, 3)  // 使用3個(gè)worker
    fmt.Println("Worker Pool求和:", sum3)  // 輸出: 55
}

func sumWithWorkerPool(numbers []int, workerCount int) int {
    tasks := make(chanint, len(numbers))
    results := make(chanint, len(numbers))
    var wg sync.WaitGroup
    
    // 啟動(dòng)worker
    for i := 0; i < workerCount; i++ {
        wg.Add(1)
        gofunc() {
            defer wg.Done()
            for n := range tasks {
                results <- n  // 實(shí)際場(chǎng)景中這里可能有更復(fù)雜的計(jì)算
            }
        }()
    }
    
    // 發(fā)送任務(wù)
    for _, n := range numbers {
        tasks <- n
    }
    close(tasks)
    
    // 等待所有worker完成
    gofunc() {
        wg.Wait()
        close(results)
    }()
    
    // 收集結(jié)果
    sum := 0
    for n := range results {
        sum += n
    }
    return sum
}

深入理解for range的兩種行為

for range在Go中有兩種完全不同的用法:

// 1. 遍歷切片/數(shù)組/映射
for i, v := range slice {
    // i是索引,v是值副本
    // 循環(huán)次數(shù)在開始時(shí)確定
}

// 2. 從Channel接收值
for value := range ch {
    // 不斷從ch接收值,直到channel被關(guān)閉
    // 如果ch未被關(guān)閉,這里會(huì)永久阻塞
}

重要區(qū)別:遍歷Channel時(shí),循環(huán)不會(huì)預(yù)先知道次數(shù),而是持續(xù)接收直到發(fā)送方關(guān)閉channel。忘記關(guān)閉channel是常見的錯(cuò)誤來(lái)源。

選擇鎖還是Channel:決策指南

編寫并發(fā)代碼時(shí),可以問(wèn)自己以下幾個(gè)問(wèn)題來(lái)做出選擇:

  1. 數(shù)據(jù)是共享的還是傳遞的?
  • 如果是共享的(多個(gè)goroutine需要訪問(wèn)同一數(shù)據(jù)),考慮使用鎖
  • 如果是傳遞的(數(shù)據(jù)從一個(gè)goroutine流向另一個(gè)),優(yōu)先使用Channel
  1. 操作是同步的還是異步的?
  • 需要嚴(yán)格同步的操作,無(wú)緩沖Channel是好的選擇

  • 允許一定異步性的操作,可以考慮緩沖Channel或鎖

  1. 復(fù)雜程度如何?

  • 簡(jiǎn)單計(jì)數(shù)器更新:考慮sync/atomic

  • 中等復(fù)雜度的數(shù)據(jù)流:Channel通常更清晰

  • 復(fù)雜的共享狀態(tài)管理:可能需要鎖或sync包中的其他工具

最佳實(shí)踐總結(jié)

  1. 優(yōu)先使用Channel:遵循Go的哲學(xué),用通信代替共享內(nèi)存
  2. 正確管理goroutine生命周期:總是使用WaitGroup或context來(lái)確保goroutine正確退出
  3. 及時(shí)關(guān)閉Channel:由發(fā)送方負(fù)責(zé)關(guān)閉channel,避免接收方永久阻塞
  4. 緩沖大小要合理:緩沖Channel可以提高性能,但過(guò)大的緩沖會(huì)浪費(fèi)內(nèi)存
  5. 考慮使用Worker Pool:對(duì)于大量任務(wù),使用固定數(shù)量的goroutine作為worker

一個(gè)簡(jiǎn)單的決策流程

當(dāng)你寫并發(fā)代碼時(shí),可以遵循這個(gè)流程:

是否需要共享狀態(tài)?
    ├── 否 → 使用Channel傳遞數(shù)據(jù)
    └── 是 → 是否可以拆分為獨(dú)立任務(wù)?
              ├── 是 → 使用Channel + 結(jié)果合并
              └── 否 → 使用鎖保護(hù)共享狀態(tài)

記住Go并發(fā)設(shè)計(jì)的黃金法則:通過(guò)通信共享內(nèi)存,而不是通過(guò)共享內(nèi)存進(jìn)行通信。優(yōu)先使用Channel來(lái)組織數(shù)據(jù)流,只有在確實(shí)需要時(shí)才使用鎖。這樣寫出的代碼不僅更安全,也更具有Go語(yǔ)言的特色。

最后自檢:寫完并發(fā)代碼后,問(wèn)問(wèn)自己:"如果兩個(gè)goroutine同時(shí)執(zhí)行這段代碼,它們會(huì)沖突嗎?"

  • 會(huì)沖突?添加同步機(jī)制(鎖或Channel)
  • 不會(huì)沖突?保持現(xiàn)狀
責(zé)任編輯:武曉燕 來(lái)源: 王中陽(yáng)
相關(guān)推薦

2023-11-10 10:51:15

Python

2010-08-20 09:46:52

云計(jì)算SaaS

2024-12-04 09:41:06

2023-10-24 08:53:24

FutureTas并發(fā)編程

2018-11-05 11:22:19

2018-08-06 14:18:09

Linux應(yīng)用程序技術(shù)

2022-10-10 11:15:21

領(lǐng)域模型軟件

2022-09-26 13:10:17

JavaScriptthis

2022-09-22 14:55:31

前端JavaScripthis

2023-08-04 08:25:03

客戶配置Spring

2024-09-06 10:48:13

2024-06-19 10:08:34

GoChannel工具

2021-01-22 10:27:28

人工智能機(jī)器學(xué)習(xí)技術(shù)

2018-09-29 15:34:34

JavaList接口

2020-06-04 14:15:55

Java中BigDecimal函數(shù)

2021-11-26 08:07:16

MySQL SQL 語(yǔ)句數(shù)據(jù)庫(kù)

2021-01-07 08:29:46

Java淺拷貝深拷貝

2021-07-02 06:54:45

GoJavachannel

2021-09-30 09:21:28

Go語(yǔ)言并發(fā)編程

2023-05-29 09:25:38

GolangSelect
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

在线影视一区| 亚洲欧美一区二区三区孕妇| 亚洲国产天堂久久综合| 亚洲最大的成人网| 欧美精品总汇| 久热99视频在线观看| 午夜精品影视国产一区在线麻豆| 日韩中文字幕精品视频| 北条麻妃在线视频观看| 久久香蕉av| 91免费国产在线观看| 91免费在线视频| 希岛爱理一区二区三区av高清| 色视频一区二区| 欧美大尺度做爰床戏| 狠狠v欧美v日韩v亚洲ⅴ| 婷婷精品进入| 97精品久久久| 久久99久久99精品免视看婷婷 | a级高清视频欧美日韩| 久久久综合av| 亚洲精品国产精品国产自| 国产丝袜在线| 天堂久久久久va久久久久| 午夜精品久久久久久久久久久久| 国产精品欧美在线观看| 欧美一级电影网站| 中文字幕视频在线观看| 国产成人高清在线| 国产亚洲欧美另类一区二区三区| 中文字幕区一区二区三| 亚洲一区国产精品| 在线毛片网站| 亚洲国产精品久久久| 中文字幕21页在线看| www国产精品com| 亚洲男人都懂第一日本| 成人欧美一区二区三区黑人| 亚洲欧洲一级| 亚洲小视频在线播放| 国产日韩在线不卡| 亚洲52av| 亚洲第一区第二区| 久久九九精品视频| 国产高清视频一区三区| 影音先锋在线一区| 97av中文字幕| 亚洲国产精品ⅴa在线观看| 4hu永久免费入口| 色诱亚洲精品久久久久久| www.久久ai| 久久99久国产精品黄毛片入口| 成人高清电影网站| 午夜午夜精品一区二区三区文| 懂色中文一区二区在线播放| 欧美亚洲综合另类| 欲香欲色天天天综合和网| 久久久久久有精品国产| 欧美日韩亚洲一区三区| 日韩精品手机在线观看| 亚洲欧美日韩国产成人精品影院| av播放在线| 免费97视频在线精品国自产拍| 99精品国产一区二区三区| 一区二区三区av在线| 亚洲视频一区二区在线观看| 米奇777四色精品人人爽| 美乳少妇欧美精品| 欧美精品综合| 日日夜夜天天综合入口| 中文字幕在线亚洲| 亚洲成人中文| 免费高清在线观看免费| 欧美日韩视频专区在线播放| 日本在线高清| 成人福利在线视频| 国产精品国产馆在线真实露脸| 日本高清成人vr专区| 色偷偷av亚洲男人的天堂| 日韩中文首页| 欧美这里只有精品| 日本精品一区二区三区高清 | 亚洲日本中文字幕区| 男男gaygays亚洲| 久久蜜桃一区二区| 亚洲一区二区欧美日韩| www.黄在线观看| 欧美成人黄色小视频| 亚洲国产专区校园欧美| 亚洲综合在线网站| 精品不卡在线视频| 亚洲国产一区二区在线观看| 波多野结衣乳巨码无在线| 欧美久久久一区| 成人精品视频| 狠狠操精品视频| 亚洲乱亚洲乱妇无码| 亚洲三级观看| 在线免费看黄色| 国模叶桐国产精品一区| 国产在线国偷精品免费看| 毛片在线播放网址| 日韩av手机在线观看| 91麻豆国产福利精品| 成人bbav| 日韩欧中文字幕| 在线黄色的网站| 黄色国产在线| 在线观看亚洲专区| 91嫩草精品| 青青草影院在线观看| 欧美性色xo影院| 美日韩中文字幕| 日韩人在线观看| 中文字幕国产精品| 成人美女在线视频| 蜜桃视频在线观看网站| 欧美精品在线播放| 麻豆久久一区二区| 国产精品白嫩美女在线观看| 亚洲丰满少妇videoshd| 老司机深夜福利在线观看| 91老司机在线| 国产精品超碰97尤物18| 国产精品qvod| 国内精品久久国产| 国产亚洲精品福利| 少妇视频一区| 蜜桃久久影院| 欧美专区亚洲专区| 婷婷激情图片久久| 91九色porny在线| 久久久人成影片一区二区三区| 国产麻豆欧美日韩一区| gogo久久| 亚洲欧洲免费无码| 日韩一区二区三区av| 亚洲一区二区成人| 麻豆电影在线播放| 久久综合福利| 日韩午夜中文字幕| 欧美一区二区精品在线| 91最新国产视频| 日韩毛片视频在线看| 久久av偷拍| 奇米精品一区二区三区| 国产亚洲激情在线| 成人国产一区二区三区精品| 黄页网站在线| 在线免费一区| 日韩精品在线免费观看| 国内精品免费**视频| 9i看片成人免费高清| 一本久道久久综合| 亚洲免费成人av电影| 国产精品夜夜爽| 成人精品视频在线观看| 国产亚洲综合视频| 久久久久中文字幕2018| 一区二区三区四区高清精品免费观看| 国产99亚洲| 国产视频精品久久| 午夜精品一区二区三区四区 | www.午夜精品| 国产欧美精品一区二区色综合| 第一区第二区在线| 在线播放你懂得| 欧美一区二区三区精美影视| 亚洲欧美精品在线| 1024成人网色www| 欧美日韩mv| 韩日精品一区二区| 成人在线电影网| 国产精品免费一区二区三区观看 | 亚洲国产精品中文| 久久色在线视频| 亚洲破处大片| 欧美黄色视屏| 一区二区三区国产免费| 91免费国产网站| 日韩精品亚洲元码| 有坂深雪av一区二区精品| 亚洲专区一区| 亚洲精品不卡在线观看| 视频二区在线| 久久久久久av无码免费网站下载| 欧美性受xxxx黑人猛交| 欧美精品丝袜久久久中文字幕| 成人av电影免费在线播放| 成人羞羞视频播放网站| 国产乱妇乱子在线播视频播放网站| 大伊香蕉精品视频在线| 国产精品毛片a∨一区二区三区|国| 欧美高清性hdvideosex| 国产三级精品三级在线专区| 一区二区三区 在线观看视| 中文字幕第100页| 精品久久久久久综合日本| 在线亚洲午夜片av大片| 性久久久久久久|