溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么在golang中利用Sync.WaitGroup解決等待問題

發(fā)布時間:2021-04-27 15:59:52 來源:億速云 閱讀:172 作者:Leah 欄目:開發(fā)技術(shù)

怎么在golang中利用Sync.WaitGroup解決等待問題?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

golang適合做什么

golang可以做服務(wù)器端開發(fā),但golang很適合做日志處理、數(shù)據(jù)打包、虛擬機(jī)處理、數(shù)據(jù)庫代理等工作。在網(wǎng)絡(luò)編程方面,它還廣泛應(yīng)用于web應(yīng)用、API應(yīng)用等領(lǐng)域。

面對goroutine我們都需要等待它完成交給它的事情,等待它計算完成或是執(zhí)行完畢,所以不得不在程序需要等待的地方使用time.Sleep()來睡眠一段時間,等待其他goroytine執(zhí)行完畢,下面的代碼打印1到100的for循環(huán)可以在很快的時間內(nèi)運(yùn)行完畢,但是我們必須添加time.Sleep()來等待其打印完畢,如果我們不等待仿佛什么也沒有發(fā)生一樣。。。。。這肯定不是我們想要的!

func main(){
    for i := 0; i < 100 ; i++{
        go fmt.Println(i)
    }
    time.Sleep(time.Second)
}

這就是我最開始學(xué)習(xí)goroutine想到的笨方法。。。。死等! 等不到 time.Second*2 在等不到*3

但是對于我們實(shí)際的使用場景來說,1秒也許等待的時間有點(diǎn)長,而有時候是遠(yuǎn)遠(yuǎn)不夠的,最主要的我們無法預(yù)知gorotine的運(yùn)行時間長短,所以也無法指定Sleep睡眠的時間。

正好前幾天學(xué)習(xí)了channel,那么現(xiàn)在我們就可以

使用channel來達(dá)到我們的目的了

func main() {
    c := make(chan bool, 100)
    for i := 0; i < 100; i++ {
        go func(i int) {
            fmt.Println(i)
            c <- true
        }(i)
    }
 
    for i := 0; i < 100; i++ {
        <-c
    }
}

完美輸出!這里我們可以從控制臺看到我們的勝利成果,但是我們有更好的處理方式:sync.WaitGroup

WaitGroup對象內(nèi)部有一個計數(shù)器,最初從0開始,它有三個方法:Add() Done() Wait() 用來控制計數(shù)器的數(shù)量從而達(dá)到等待的目的。

func main() {
   wg := sync.WaitGroup{}
   wg.Add(100)
   for i := 0; i < 100; i++ {
      go func(i int) {
         defer wg.Done()
         fmt.Print(i)
      }(i)
   }
   wg.Wait()
}

首先使用 add(100) 把計數(shù)器的數(shù)量設(shè)置為100,每次循環(huán)運(yùn)行完畢都把計數(shù)器減1,主函數(shù)使用Wait()

阻塞,直到計數(shù)器歸0結(jié)束主線程結(jié)束阻塞。

這里會阻塞主線程!!!需要注意。

另外:以上三種方式打印出的數(shù)字皆無序

type WaitGroup struct {
 noCopy noCopy
 state1 [3]uint32
}

它只是一個struct所以當(dāng)參數(shù)傳遞的時候需要傳遞指針。

補(bǔ)充:sync.WaitGroup的錯誤用法與糾正

本文主要是記錄在學(xué)習(xí)golang語言過程中錯誤使用sync.WaitGroup以及指針引起的報錯,并且糾正過程。

1.錯誤使用場景

main包的實(shí)現(xiàn)內(nèi)容:

package main
import (
    "fmt"
    "pressure/game"
    "sync"
)
var waitGroup sync.WaitGroup //定義一個同步等待的組
func main(){
    waitGroup.Add(1) //添加一個計數(shù)
    go game.ConnSocket(serverAddr, waitGroup) //調(diào)用其他包的方法執(zhí)行任務(wù)
    waitGroup.Wait() //阻塞直到所有任務(wù)完成
    fmt.Println("main DONE!!!")
}

如下是game包的內(nèi)容:

package game
import (
    "fmt"
    "net"
    "strconv"
    "strings"
    "sync"
)
var gameWait sync.WaitGroup
func ConnSocket(serverAddr string, wait sync.WaitGroup) {
    var err error
    Conn, err = net.Dial("tcp", serverAddr)
    if err != nil {
        fmt.Println("Error connected:", err.Error())
        wait.Done()
        return
    }
    gameWait = wait
    fmt.Println("connected OK:", Conn.RemoteAddr())
    go readMessage()
}
func readMessage() {
    fmt.Println("readMessage:")
    for {
        if OK:=dosomething(),OK{
            gameWait.Done()
        }
    }
}

然后執(zhí)行如上的代碼,會出現(xiàn)報錯:

panic: sync: negative WaitGroup counter

goroutine 12 [running]:

sync.(*WaitGroup).Add(0x7b8b90, 0xffffffffffffffff)

D:/Program Files/Go/src/sync/waitgroup.go:75 +0x1d0

sync.(*WaitGroup).Done(0x7b8b90)

原因:

func ConnSocket(serverAddr string, wait sync.WaitGroup)只是把sync.WaitGroup的值傳遞過來,而非內(nèi)存地址,所以在賦值給gameWait的時候也只是賦值了值,gameWait并非指向的main里的waitGroup 的內(nèi)存地址,導(dǎo)致 gameWait.Done()的時候并沒有獲取到main里的計數(shù)1,所以才會報負(fù)數(shù)。究其原因還是沒有對指針的理解。所以針對上面的問題做了修改。

2.正確的使用

main包的實(shí)現(xiàn)內(nèi)容:

package main
import (
    "fmt"
    "pressure/game"
    "sync"
)
var waitGroup sync.WaitGroup //定義一個同步等待的組
func main(){
    waitGroup.Add(1) //添加一個計數(shù)
    //這里傳遞waitGroup的內(nèi)存地址
    go game.ConnSocket(serverAddr, &waitGroup) //調(diào)用其他包的方法執(zhí)行任務(wù)
    waitGroup.Wait() //阻塞直到所有任務(wù)完成
    fmt.Println("main DONE!!!")
}

如下是game包的內(nèi)容:

package game
import (
    "fmt"
    "net"
    "strconv"
    "strings"
    "sync"
)
var gameWait *sync.WaitGroup//此處也申明為指針變量
//參數(shù)傳遞為指針傳遞*sync.WaitGroup,即wait 指向的是main里的waitGroup的內(nèi)存地址
func ConnSocket(serverAddr string, wait *sync.WaitGroup) {
    var err error
    Conn, err = net.Dial("tcp", serverAddr)
    if err != nil {
        fmt.Println("Error connected:", err.Error())
        wait.Done()
        return
    }
    gameWait = wait//指針指向另一個指針,這樣就做到了gameWait指向的是wait的地址,所以gameWait修改wait的內(nèi)存地址鎖存貯的值也會跟隨改變
    fmt.Println("connected OK:", Conn.RemoteAddr())
    go readMessage()
}
func readMessage() {
    fmt.Println("readMessage:")
    for {
        if OK:=dosomething(),OK{
            gameWait.Done()
        }
    }
}

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI