如何在不使用时间的情况下等待所有goroutine完成.睡眠?

Dan*_*nte 92 synchronization go goroutine

此代码选择同一文件夹中的所有xml文件,与调用的可执行文件一样,并且异步地将处理应用于回调方法中的每个结果(在下面的示例中,只打印出文件的名称).

如何避免使用sleep方法来保持main方法退出?我有问题缠绕我的头围绕通道(我认为这是需要的,同步结果)所以任何帮助表示赞赏!

package main

import (
    "fmt"
    "io/ioutil"
    "path"
    "path/filepath"
    "os"
    "runtime"
    "time"
)

func eachFile(extension string, callback func(file string)) {
    exeDir := filepath.Dir(os.Args[0])
    files, _ := ioutil.ReadDir(exeDir)
    for _, f := range files {
            fileName := f.Name()
            if extension == path.Ext(fileName) {
                go callback(fileName)
            }
    }
}


func main() {
    maxProcs := runtime.NumCPU()
    runtime.GOMAXPROCS(maxProcs)

    eachFile(".xml", func(fileName string) {
                // Custom logic goes in here
                fmt.Println(fileName)
            })

    // This is what i want to get rid of
    time.Sleep(100 * time.Millisecond)
}
Run Code Online (Sandbox Code Playgroud)

zzz*_*zzz 148

您可以使用sync.WaitGroup.引用链接的示例:

package main

import (
        "net/http"
        "sync"
)

func main() {
        var wg sync.WaitGroup
        var urls = []string{
                "http://www.golang.org/",
                "http://www.google.com/",
                "http://www.somestupidname.com/",
        }
        for _, url := range urls {
                // Increment the WaitGroup counter.
                wg.Add(1)
                // Launch a goroutine to fetch the URL.
                go func(url string) {
                        // Decrement the counter when the goroutine completes.
                        defer wg.Done()
                        // Fetch the URL.
                        http.Get(url)
                }(url)
        }
        // Wait for all HTTP fetches to complete.
        wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

  • sat,是的,有一个原因,它在sync.WaitGroup.Add文档中描述:```注意带有正delta的调用必须在调用Wait之前发生,否则Wait可能会等待一个太小的组.通常,这意味着对Add的调用应该在创建要等待的goroutine或其他事件的语句之前执行.请参阅WaitGroup示例.`` (16认同)
  • 调整此代码导致我进行了长时间的调试会话,因为我的goroutine是一个命名函数,并且将WaitGroup作为值传入将复制它并使wg.Done()无效.虽然可以通过传递指针和wg来解决这个问题,但是防止此类错误的更好方法是将WaitGroup变量声明为第一个指针:`wg:= new(sync.WaitGroup)`而不是`var wg sync. WaitGroup`. (12认同)
  • 你有什么理由去wg.Add(1)在go例程之外吗?我们可以在延迟wg.Done()之前进行吗? (10认同)
  • 我想在“for _, url := range urls”行上方写“wg.Add(len(urls))”是有效的,我相信更好,因为您只使用一次“添加”。 (2认同)

jos*_*hlf 52

WaitGroups绝对是执行此操作的规范方式.但是,为了完整起见,这里是WaitGroups引入之前常用的解决方案.基本的想法是使用一个频道说"我已经完成",并让主要的goroutine等到每个衍生例程报告完成.

func main() {
    c := make(chan struct{}) // We don't need any data to be passed, so use an empty struct
    for i := 0; i < 100; i++ {
        go func() {
            doSomething()
            c <- struct{}{} // signal that the routine has completed
        }()
    }

    // Since we spawned 100 routines, receive 100 messages.
    for i := 0; i < 100; i++ {
        <- c
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很高兴看到普通渠道的解决方案.一个额外的好处:如果`doSomething()`返回一些结果,那么你可以将它放在通道上,你可以在第二个for循环中收集并处理结果(一旦它们准备就绪) (8认同)
  • 它只有在您已经知道您想要开始的多糖量时才有效.如果您正在编写某种html爬虫并以递归方式为页面上的每个链接启动gorutine,该怎么办? (2认同)

dim*_*mmg 11

sync.WaitGroup可以在这里为您提供帮助。

package main

import (
    "fmt"
    "sync"
    "time"
)


func wait(seconds int, wg * sync.WaitGroup) {
    defer wg.Done()

    time.Sleep(time.Duration(seconds) * time.Second)
    fmt.Println("Slept ", seconds, " seconds ..")
}


func main() {
    var wg sync.WaitGroup

    for i := 0; i <= 5; i++ {
        wg.Add(1)   
        go wait(i, &wg)
    }
    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)