简体   繁体   English

当我在goroutine中运行wg.Wait()时,为什么我的代码正常工作?

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

I have a list of urls that I am scraping. 我有一个我正在抓取的网址列表。 What I want to do is store all of the successfully scraped page data into a channel, and when I am done, dump it into a slice. 我想要做的是将所有成功抓取的页面数据存储到一个通道中,当我完成后,将其转储到一个切片中。 I don't know how many successful fetches I will get, so I cannot specify a fixed length. 我不知道我会获得多少成功的提取,所以我不能指定固定的长度。 I expected the code to reach wg.Wait() and then wait until all the wg.Done() methods are called, but I never reached the close(queue) statement. 我希望代码到达wg.Wait()然后等到调用所有wg.Done()方法,但我从未到达close(queue)语句。 Looking for a similar answer, I came across this SO answer 寻找类似的答案,我遇到了这个答案

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

where the author does something similar: 作者做类似的事情:

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)
}()

As soon as I wrapped my wg.Wait() inside the goroutine, close(queue) was reached: 一旦我将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)
}

Why does the code not reach the close if I don't use a goroutine for wg.Wait() ? 如果我不为wg.Wait()使用goroutine,为什么代码没有达到close wg.Wait() I would think that the all of the defer wg.Done() statements are called so eventually it would clear up, because it gets to the wg.Wait() . 我认为所有defer wg.Done()语句都被调用,所以最终它会清理,因为它到达了wg.Wait() Does it have to do with receiving values in my channel? 它与我的频道中的接收值有关吗?

You need to wait for goroutines to finish in a separate thread because queue needs to be read from. 您需要等待goroutine在单独的线程中完成,因为需要读取queue When you do the following: 执行以下操作时:

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)
}

Each goroutine blocks at queue <- activity since queue is unbuffered and nothing is reading data from it. 每个goroutine阻塞queue <- activity因为queue是无缓冲的,没有任何东西正在从中读取数据。 This is because the range loop on queue is in the main thread after wg.Wait . 这是因为queue的范围循环在wg.Wait之后的主线程中。

wg.Wait will only unblock once all the goroutine return. wg.Wait只会在所有goroutine返回后解锁。 But as mentioned, all the goroutines are blocked at channel send. 但如上所述,所有goroutine都在通道发送时被阻止。

When you use a separate goroutine to wait, code execution actually reaches the range loop on queue . 当您使用单独的goroutine等待时,代码执行实际上会到达queue的范围循环。

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

This results in the goroutines unblocking at the queue <- activity statement (main thread starts reading off queue ) and running until completion. 这导致goroutines在queue <- activity语句中解锁(主线程开始读取queue )并运行直到完成。 Which in turn calls each individual wg.Done . 反过来又调用每个人wg.Done

Once the waiting goroutine get past wg.Wait , queue is closed and the main thread exits the range loop on it. 一旦等待的goroutine过了wg.Waitqueue就会关闭,主线程退出它的范围循环。

queue channel is unbuffered so every goroutine trying to write to it gets blocked because reader process is not yet started. queue通道是无缓冲的,因此每个尝试写入它的goroutine都会被阻止,因为读取器进程尚未启动。 So no goroutinte can write and they all hang - as a result wg.Wait waits forever. 所以没有goroutinte可以写,并且它们都挂起 - 因此wg.Wait等待永远。 Try to launch reader in a separate goroutine: 尝试在单独的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)
    }
}()

and then start waiter: 然后开始服务员:

wg.Wait() 
close(queue)

This way you can not to accumulate all the data in channel and overload it, but get data as it comes and put to target slice. 这样,您就不能在通道中累积所有数据并使其超载,而是在数据到达时将数据放入目标切片。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我的异步函数仅在更改被调用函数的语法后才能正常工作? - Why does my Async function only work correctly when I change my called functions syntax? 为什么我的异步代码在调试时同步运行? - Why Does My Asynchronous Code Run Synchronously When Debugging? 为什么我的代码无法等待异步回调完成? - Why doesn't work my code to wait for asynchronous callbacks to complete? 为什么 xmlhttprequest 中的代码在 async 标志设置为 false 时有效,但在设置为 true 时无效 - Why does the code inside xmlhttprequest work when async flag is set to false but not when it is set to true 我该如何等待异步猕猴桃测试完成或在测试完成后运行代码? - How can I wait for my asynchronous Kiwi tests to complete, or run code after they do? 使用 UseState 钩子等待 State 更新,然后在函数中运行代码 - Wait for State to update using UseState hook and then run code inside a funciton 为什么我的 CompletableFuture 代码在 Java 8 中运行,但在 Java 11 中没有运行? - Why does my CompletableFuture code run in Java 8 but not in Java 11? 如何使C#异步和等待代码正常工作? - How do I get my C# Async and Wait code to work? 为什么我的 async/await 示例不起作用? - Why does my async/await example not work? 为什么我的超时要等到等待完成后才出现,而我的日志却没有? - Why does my timeout wait until after the await completes but my log does not?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM