[英]Why does my Async function only work correctly when I change my called functions syntax?
[英]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.Wait
, queue
就會關閉,主線程退出它的范圍循環。
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.