簡體   English   中英

當我在goroutine中運行wg.Wait()時,為什么我的代碼正常工作?

[英]Why does my code work correctly when I run wg.Wait() inside a goroutine?

我有一個我正在抓取的網址列表。 我想要做的是將所有成功抓取的頁面數據存儲到一個通道中,當我完成后,將其轉儲到一個切片中。 我不知道我會獲得多少成功的提取,所以我不能指定固定的長度。 我希望代碼到達wg.Wait()然后等到調用所有wg.Done()方法,但我從未到達close(queue)語句。 尋找類似的答案,我遇到了這個答案

https://stackoverflow.com/a/31573574/5721702

作者做類似的事情:

ports := make(chan string)
toScan := make(chan int)
var wg sync.WaitGroup

// make 100 workers for dialing
for i := 0; i < 100; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        for p := range toScan {
            ports <- worker(*host, p)
        }
    }()
}

// close our receiving ports channel once all workers are done
go func() {
    wg.Wait()
    close(ports)
}()

一旦我將wg.Wait()包裝在goroutine中,就會到達close(queue)

urls := getListOfURLS()
activities := make([]Activity, 0, limit)
queue := make(chan Activity)
for i, activityURL := range urls {
    wg.Add(1)
    go func(i int, url string) {
        defer wg.Done()
        activity, err := extractDetail(url)
        if err != nil {
            log.Println(err)
            return
        }
        queue <- activity
    }(i, activityURL)
}
    // calling it like this without the goroutine causes the execution to hang
// wg.Wait() 
// close(queue)

    // calling it like this successfully waits
go func() {
    wg.Wait()
    close(queue)
}()
for a := range queue {
    // block channel until valid url is added to queue
    // once all are added, close it
    activities = append(activities, a)
}

如果我不為wg.Wait()使用goroutine,為什么代碼沒有達到close wg.Wait() 我認為所有defer wg.Done()語句都被調用,所以最終它會清理,因為它到達了wg.Wait() 它與我的頻道中的接收值有關嗎?

您需要等待goroutine在單獨的線程中完成,因為需要讀取queue 執行以下操作時:

queue := make(chan Activity)
for i, activityURL := range urls {
    wg.Add(1)
    go func(i int, url string) {
        defer wg.Done()
        activity, err := extractDetail(url)
        if err != nil {
            log.Println(err)
            return
        }
        queue <- activity // nothing is reading data from queue.
    }(i, activityURL)
}

wg.Wait() 
close(queue)

for a := range queue {
    activities = append(activities, a)
}

每個goroutine阻塞queue <- activity因為queue是無緩沖的,沒有任何東西正在從中讀取數據。 這是因為queue的范圍循環在wg.Wait之后的主線程中。

wg.Wait只會在所有goroutine返回后解鎖。 但如上所述,所有goroutine都在通道發送時被阻止。

當您使用單獨的goroutine等待時,代碼執行實際上會到達queue的范圍循環。

// wg.Wait does not block the main thread.
go func() {
    wg.Wait()
    close(queue)
}()

這導致goroutines在queue <- activity語句中解鎖(主線程開始讀取queue )並運行直到完成。 反過來又調用每個人wg.Done

一旦等待的goroutine過了wg.Waitqueue就會關閉,主線程退出它的范圍循環。

queue通道是無緩沖的,因此每個嘗試寫入它的goroutine都會被阻止,因為讀取器進程尚未啟動。 所以沒有goroutinte可以寫,並且它們都掛起 - 因此wg.Wait等待永遠。 嘗試在單獨的goroutine中啟動閱讀器:

go func() {
    for a := range queue {
        // block channel until valid url is added to queue
        // once all are added, close it
       activities = append(activities, a)
    }
}()

然后開始服務員:

wg.Wait() 
close(queue)

這樣,您就不能在通道中累積所有數據並使其超載,而是在數據到達時將數據放入目標切片。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM