递归Goroutines,告诉Go停止从频道阅读的最新方法是什么?

Sam*_*ums 10 concurrency recursion go goroutine

我想知道解决这个问题的惯用方法(目前抛出死锁错误),递归分支未知次数,所以我不能简单地关闭通道.

http://play.golang.org/p/avLf_sQJj_

我通过将指针传递给一个数字并递增它来使它工作,我已经研究过使用同步等待组.我没有感觉(我可能错了),我想出了一个优雅的解决方案.我见过的Go示例往往简单,聪明,简洁.

这是Tour of Go的最后一个练习,https://tour.golang.org/#73

你知道'Go程序员怎么会'管理这个?任何帮助,将不胜感激.我想从一开始就学好.

tom*_*asz 2

sync.WaitGroup您可以扩展在解析的 url 上发送的结果并包含找到的新 URL 的数量,而不是涉及。在主循环中,只要有东西要收集,您就会继续读取结果。

\n\n

在您的情况下,找到的 url 数量将是生成的 go 例程的数量,但不一定需要如此。我个人会生成或多或少固定数量的获取例程,因此您不会打开太多 HTTP 请求(或者至少您可以控制它)。那么你的主循环不会改变,因为它不关心提取是如何执行的。这里重要的事实是,您需要为每个 url \xe2\x80\x93 发送结果或错误。我已经修改了此处的代码,因此当深度已经为 1 时,它不会生成新的例程。

\n\n

该解决方案的一个副作用是您可以轻松地打印主循环中的进度。

\n\n

这是操场上的示例:

\n\n

http://play.golang.org/p/BRlUc6bojf

\n\n
package main\n\nimport (\n    "fmt"\n)\n\ntype Fetcher interface {\n    // Fetch returns the body of URL and\n    // a slice of URLs found on that page.\n    Fetch(url string) (body string, urls []string, err error)\n}\n\ntype Res struct {\n    url string\n    body string\n    found int // Number of new urls found\n}\n\n// Crawl uses fetcher to recursively crawl\n// pages starting with url, to a maximum of depth.\nfunc Crawl(url string, depth int, fetcher Fetcher, ch chan Res, errs chan error, visited map[string]bool) {\n    body, urls, err := fetcher.Fetch(url)\n    visited[url] = true\n    if err != nil {\n        errs <- err\n        return\n    }\n\n    newUrls := 0    \n    if depth > 1 {\n        for _, u := range urls {\n            if !visited[u] {\n                newUrls++\n                go Crawl(u, depth-1, fetcher, ch, errs, visited)\n            }\n        }\n    }\n\n    // Send the result along with number of urls to be fetched\n    ch <- Res{url, body, newUrls}\n\n    return\n}\n\nfunc main() {\n    ch := make(chan Res)\n    errs := make(chan error)\n    visited := map[string]bool{}\n    go Crawl("http://golang.org/", 4, fetcher, ch, errs, visited)\n    tocollect := 1\n    for n := 0; n < tocollect; n++ {\n        select {\n        case s := <-ch:\n            fmt.Printf("found: %s %q\\n", s.url, s.body)\n            tocollect += s.found\n        case e := <-errs:\n            fmt.Println(e)\n        }\n    }\n\n}\n\n// fakeFetcher is Fetcher that returns canned results.\ntype fakeFetcher map[string]*fakeResult\n\ntype fakeResult struct {\n    body string\n    urls []string\n}\n\nfunc (f fakeFetcher) Fetch(url string) (string, []string, error) {\n    if res, ok := f[url]; ok {\n        return res.body, res.urls, nil\n    }\n    return "", nil, fmt.Errorf("not found: %s", url)\n}\n\n// fetcher is a populated fakeFetcher.\nvar fetcher = fakeFetcher{\n    "http://golang.org/": &fakeResult{\n        "The Go Programming Language",\n        []string{\n            "http://golang.org/pkg/",\n            "http://golang.org/cmd/",\n        },\n    },\n    "http://golang.org/pkg/": &fakeResult{\n        "Packages",\n        []string{\n            "http://golang.org/",\n            "http://golang.org/cmd/",\n            "http://golang.org/pkg/fmt/",\n            "http://golang.org/pkg/os/",\n        },\n    },\n    "http://golang.org/pkg/fmt/": &fakeResult{\n        "Package fmt",\n        []string{\n            "http://golang.org/",\n            "http://golang.org/pkg/",\n        },\n    },\n    "http://golang.org/pkg/os/": &fakeResult{\n        "Package os",\n        []string{\n            "http://golang.org/",\n            "http://golang.org/pkg/",\n        },\n    },\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

是的,遵循@jimt的建议并确保对地图线程的访问是安全的。

\n