首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >围棋练习#10之旅:爬虫

围棋练习#10之旅:爬虫
EN

Stack Overflow用户
提问于 2012-11-04 17:49:23
回答 21查看 11.8K关注 0票数 23

我正在经历Go Tour,我觉得除了并发性之外,我对这门语言有很好的理解。

slide 10是一个练习,它要求读者对网络爬虫进行并行化(并使其不包含重复,但我还没有做到这一点)。

这是我到目前为止所知道的:

代码语言:javascript
运行
复制
func Crawl(url string, depth int, fetcher Fetcher, ch chan string) {
    if depth <= 0 {
        return
    }

    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        ch <- fmt.Sprintln(err)
        return
    }

    ch <- fmt.Sprintf("found: %s %q\n", url, body)
    for _, u := range urls {
        go Crawl(u, depth-1, fetcher, ch)
    }
}

func main() {
    ch := make(chan string, 100)
    go Crawl("http://golang.org/", 4, fetcher, ch)

    for i := range ch {
        fmt.Println(i)
    }
}

我的问题是,我应该把close(ch)调用放在哪里。

如果我将一个defer close(ch)放在Crawl方法中的某个地方,那么程序最终会从一个衍生的goroutine写入关闭的通道,因为对Crawl的调用将在衍生的goroutine返回之前返回。

如果我省略了对close(ch)的调用,正如我所演示的那样,程序会在确定通道范围的主函数中死锁,因为当所有goroutines都返回时,通道永远不会关闭。

EN

回答 21

Stack Overflow用户

回答已采纳

发布于 2012-11-05 06:57:43

看一看Effective Go的并行化部分,就可以得到解决方案的想法。实际上,您必须关闭函数的每个返回路径上的通道。实际上,这是defer语句的一个很好的用例:

代码语言:javascript
运行
复制
func Crawl(url string, depth int, fetcher Fetcher, ret chan string) {
    defer close(ret)
    if depth <= 0 {
        return
    }

    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        ret <- err.Error()
        return
    }

    ret <- fmt.Sprintf("found: %s %q", url, body)

    result := make([]chan string, len(urls))
    for i, u := range urls {
        result[i] = make(chan string)
        go Crawl(u, depth-1, fetcher, result[i])
    }

    for i := range result {
        for s := range result[i] {
            ret <- s
        }
    }

    return
}

func main() {
    result := make(chan string)
    go Crawl("http://golang.org/", 4, fetcher, result)

    for s := range result {
        fmt.Println(s)
    }
}

与您的代码的本质区别在于,Crawl的每个实例都有自己的返回通道,而调用者函数在其返回通道中收集结果。

票数 25
EN

Stack Overflow用户

发布于 2016-04-23 01:14:25

我选择了一个完全不同的方向。我可能被关于使用地图的提示误导了。

代码语言:javascript
运行
复制
// SafeUrlMap is safe to use concurrently.
type SafeUrlMap struct {
    v   map[string]string
    mux sync.Mutex
}

func (c *SafeUrlMap) Set(key string, body string) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    c.v[key] = body
    c.mux.Unlock()
}

// Value returns mapped value for the given key.
func (c *SafeUrlMap) Value(key string) (string, bool) {
    c.mux.Lock()
    // Lock so only one goroutine at a time can access the map c.v.
    defer c.mux.Unlock()
    val, ok := c.v[key]
    return val, ok
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, urlMap SafeUrlMap) {
    defer wg.Done()
    urlMap.Set(url, body)

    if depth <= 0 {
        return
    }

    body, urls, err := fetcher.Fetch(url)
    if err != nil {
        fmt.Println(err)
        return
    }

    for _, u := range urls {
        if _, ok := urlMap.Value(u); !ok {
            wg.Add(1)
            go Crawl(u, depth-1, fetcher, urlMap)
        }
    }

    return
}

var wg sync.WaitGroup

func main() {
    urlMap := SafeUrlMap{v: make(map[string]string)}

    wg.Add(1)
    go Crawl("http://golang.org/", 4, fetcher, urlMap)
    wg.Wait()

    for url := range urlMap.v {
        body, _ := urlMap.Value(url)
        fmt.Printf("found: %s %q\n", url, body)
    }
}
票数 13
EN

Stack Overflow用户

发布于 2016-09-22 03:18:01

O(1)在地图上查找url的时间,而不是在所有访问的url的切片上查找O(n),这将有助于最小化在临界区内花费的时间,这对于本例来说是微不足道的时间量,但将与规模相关。

WaitGroup用于阻止顶级Crawl()函数在所有子go例程完成之前返回。

代码语言:javascript
运行
复制
func Crawl(url string, depth int, fetcher Fetcher) {
    var str_map = make(map[string]bool)
    var mux sync.Mutex
    var wg sync.WaitGroup

    var crawler func(string,int)
    crawler = func(url string, depth int) {
        defer wg.Done()

        if depth <= 0 {
            return
        }   

        mux.Lock()
        if _, ok := str_map[url]; ok {
            mux.Unlock()
            return;
        }else{
            str_map[url] = true
            mux.Unlock()
        }

        body, urls, err := fetcher.Fetch(url)
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Printf("found: %s %q %q\n", url, body, urls)

        for _, u := range urls {
            wg.Add(1)
            go crawler(u, depth-1)          
        }       
    }
    wg.Add(1)
    crawler(url,depth)
    wg.Wait()   
}

func main() {
    Crawl("http://golang.org/", 4, fetcher)
}
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13217547

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档