濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 解析Go的Waitgroup和鎖的問(wèn)題

解析Go的Waitgroup和鎖的問(wèn)題

熱門標(biāo)簽:開(kāi)通400電話申請(qǐng)流程 400手機(jī)電話免費(fèi)辦理 武漢百應(yīng)人工智能電銷機(jī)器人 如何利用高德地圖標(biāo)注家 上海企業(yè)外呼系統(tǒng)排名 電腦外呼系統(tǒng)輻射大嗎 揚(yáng)州電銷外呼系統(tǒng)軟件 百度地圖標(biāo)注位置網(wǎng)站 智能語(yǔ)音電銷的機(jī)器人

學(xué) Go 的時(shí)候知道 Go 語(yǔ)言支持并發(fā),最簡(jiǎn)單的方法是通過(guò) go 關(guān)鍵字開(kāi)啟 goroutine 即可??稍诠ぷ髦?,用的是 sync 包的 WaitGroup,然而這樣還不夠,當(dāng)多個(gè) goroutine 同時(shí)訪問(wèn)一個(gè)變量時(shí),還要考慮如何保證這些 goroutine 之間不會(huì)相互影響,這就又使用到了 sync 的 Mutex。它們是如何串起來(lái)的呢?

一、Goroutinue

先說(shuō) goroutine,我們都知道它是 Go 中的輕量級(jí)線程。Go 程序從 main 包的 main() 函數(shù)開(kāi)始,在程序啟動(dòng)時(shí),Go 程序就會(huì)為 main() 函數(shù)創(chuàng)建一個(gè)默認(rèn)的 goroutine。使用 goroutine,使用關(guān)鍵字 go 即可。

package main
import (
    "fmt"
)
func main() {
    // 并發(fā)執(zhí)行程序
    go running()
}
func running() {
    fmt.Println("Goroutine")
}

執(zhí)行代碼會(huì)發(fā)現(xiàn)沒(méi)有我們預(yù)期的“Goroutine”輸出,這是因?yàn)楫?dāng)前的程序是一個(gè)單線程的程序,main 函數(shù)只要執(zhí)行后,就不會(huì)再管其他線程在做什么事情,程序就自動(dòng)退出了。解決辦法是加一個(gè) sleep 函數(shù),讓 main 函數(shù)等待 running 函數(shù)執(zhí)行完畢后再退出。我們假設(shè) running 函數(shù)里的代碼執(zhí)行需要 2 秒,因此讓 main 函數(shù)等待 3 秒再退出。

package main
import (
    "fmt"
    "time"
)
func main() {
    // 并發(fā)執(zhí)行程序
    go running()
    time.Sleep(3 * time.Second)
}
func running() {
    fmt.Println("Goroutine")
}

再次執(zhí)行代碼,終端輸出了我們想要的“Goroutine”字符串。

二、WaitGroup

上面我們是假設(shè)了 running 函數(shù)執(zhí)行需要 2 秒,可如果執(zhí)行需要 10 秒甚至更長(zhǎng)時(shí)間,不知道 goroutin 什么時(shí)候結(jié)束,難道還要 main 函數(shù) sleep 更多的秒數(shù)嗎?就不能讓 running 函數(shù)執(zhí)行完去通知 main 函數(shù),main 函數(shù)收到信號(hào)自動(dòng)退出嗎?還真可以!可以使用 sync 包的 Waitgroup 判斷一組任務(wù)是否完成。

WatiGroup 能夠一直等到所有的 goroutine 執(zhí)行完成,并且阻塞主線程的執(zhí)行,直到所有的 goroutine 執(zhí)行完成。它有 3 個(gè)方法:

  • Add():給計(jì)數(shù)器添加等待 goroutine 的數(shù)量。
  • Done():減少 WaitGroup 計(jì)數(shù)器的值,應(yīng)在協(xié)程的最后執(zhí)行。
  • Wait():執(zhí)行阻塞,直到所有的 WaitGroup 數(shù)量變成 0

一個(gè)簡(jiǎn)單的示例如下:

package main 
import ( 
    "fmt” 
    "sync” 
    “time"
) 

func process(i int, wg *sync.WaitGroup) { 
    fmt.Println("started Goroutine ", i) 
    time.Sleep(2 * time.Second) 
    fmt.Printf("Goroutine %d ended\n", i) 
    wg.Done() 
} 

func main() { 
    var wg sync.WaitGroup 
    for i := 0; i  3; i++ { 
        wg.Add(1) 
        go process(i, wg) 
    } 
    wg.Wait() 
    fmt.Println("All go routines finished executing”) 
}
//main函數(shù)也可以寫成如下方式
func main() {
    var wg sync.WaitGroup
    wg.Add(3) //設(shè)置計(jì)數(shù)器,數(shù)值即為goroutine的個(gè)數(shù)
    go process(1, wg)
    go process(2, wg)
    go process(3, wg)
    wg.Wait() //主goroutine阻塞等待計(jì)數(shù)器變?yōu)?
    fmt.Println("All goroutines finished executing")
}

命令行輸出如下:

deer@192 src % go run hello.go //第1次
started Goroutine  3
started Goroutine  1
started Goroutine  2
Goroutine 2 ended
Goroutine 1 ended
Goroutine 3 ended
All goroutines finished executing

deer@192 src % go run hello.go //第2次
started Goroutine  3
started Goroutine  1
started Goroutine  2
Goroutine 1 ended
Goroutine 2 ended
Goroutine 3 ended
All goroutines finished executing

deer@192 src % go run hello.go //第3次
started Goroutine  3
started Goroutine  2
started Goroutine  1
Goroutine 3 ended
Goroutine 1 ended
Goroutine 2 ended
All goroutines finished executing

簡(jiǎn)單的說(shuō),上面程序中 wg 內(nèi)部維護(hù)了一個(gè)計(jì)數(shù)器,激活了 3 個(gè) goroutine:
1)每次激活 goroutine 之前,都先調(diào)用 Add() 方法增加一個(gè)需要等待的 goroutine 計(jì)數(shù)。
2)每個(gè) goroutine 都運(yùn)行 process() 函數(shù),這個(gè)函數(shù)在執(zhí)行完成時(shí)需要調(diào)用 Done() 方法來(lái)表示 goroutine 的結(jié)束。
3)激活 3 個(gè) goroutine 后,main 的 goroutine 會(huì)執(zhí)行到 Wait(),由于每個(gè)激活的 goroutine 運(yùn)行的 process() 都需要睡眠 2 秒,所以 main 的 goroutine 在 Wait() 這里會(huì)阻塞一段時(shí)間(大約2秒),
4)當(dāng)所有 goroutine 都完成后,計(jì)數(shù)器減為 0,Wait() 將不再阻塞,于是 main 的 goroutine 得以執(zhí)行后面的 Println()。

這里需要注意:
1)process() 中使用指針類型的 *sync.WaitGroup 作為參數(shù),表示這 3 個(gè) goroutine 共享一個(gè) wg,才能知道這 3 個(gè) goroutine 都完成了。如果這里使用值類型的 sync.WaitGroup 作為參數(shù),意味著每個(gè) goroutine 都拷貝一份 wg,每個(gè) goroutine 都使用自己的 wg,main goroutine將會(huì)永久阻塞而導(dǎo)致產(chǎn)生死鎖。
2)Add() 設(shè)置的數(shù)量必須與實(shí)際等待的 goroutine 個(gè)數(shù)一致,也就是和Done的調(diào)用數(shù)量必須相等,否則會(huì)panic,報(bào)錯(cuò)信息如下:

fatal error: all goroutines are asleep - deadlock!

三、鎖

當(dāng)多個(gè) goroutine 同時(shí)操作一個(gè)變量時(shí),會(huì)存在數(shù)據(jù)競(jìng)爭(zhēng),導(dǎo)致最后的結(jié)果與期待的不符,解決辦法就是加鎖。Go 中的 sync 包 實(shí)現(xiàn)了兩種鎖:Mutex 和 RWMutex,前者為互斥鎖,后者為讀寫鎖,基于 Mutex 實(shí)現(xiàn)。當(dāng)我們的場(chǎng)景是寫操作為主時(shí),可以使用 Mutex 來(lái)加鎖、解鎖。

var lock sync.Mutex //聲明一個(gè)互斥鎖
 lock.Lock() //加鎖
//code...
 lock.Unlock() //解鎖

互斥鎖其實(shí)就是每個(gè)線程在對(duì)資源操作前都嘗試先加鎖,成功加鎖才能操作,操作結(jié)束后再解鎖。也就是說(shuō),使用了互斥鎖,同一時(shí)刻只能有一個(gè) goroutine 在執(zhí)行。

以上就是解析Go的Waitgroup和鎖的問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于Go的Waitgroup和鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • go 實(shí)現(xiàn)簡(jiǎn)易端口掃描的示例
  • go xorm框架的使用
  • Go語(yǔ)言快速入門圖文教程
  • go語(yǔ)言基礎(chǔ) seek光標(biāo)位置os包的使用
  • Go語(yǔ)言獲取文件的名稱、前綴、后綴
  • Go語(yǔ)言 如何實(shí)現(xiàn)RSA加密解密
  • Go 自定義package包設(shè)置與導(dǎo)入操作
  • 詳解Gotorch多機(jī)定時(shí)任務(wù)管理系統(tǒng)

標(biāo)簽:江西 黑龍江 張掖 武漢 宜賓 延邊 嘉峪關(guān) 新余

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《解析Go的Waitgroup和鎖的問(wèn)題》,本文關(guān)鍵詞  解析,的,Waitgroup,和,鎖,問(wèn)題,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《解析Go的Waitgroup和鎖的問(wèn)題》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于解析Go的Waitgroup和鎖的問(wèn)題的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    招远市| 洛扎县| 武胜县| 石家庄市| 山东| 皋兰县| 高雄市| 兴和县| 丹凤县| 安多县| 岑溪市| 日照市| 沙湾县| 玛多县| 灵石县| 永平县| 谷城县| 南涧| 南皮县| 噶尔县| 武强县| 潜山县| 西林县| 毕节市| 迁西县| 长子县| 万全县| 苗栗市| 曲沃县| 买车| 韩城市| 来宾市| 青岛市| 肥乡县| 乐都县| 南汇区| 五指山市| 兴隆县| 滨州市| 九龙城区| 延津县|