当前位置: 首页 > 知识库问答 >
问题:

围棋之旅练习#10:爬虫

黄元章
2023-03-14

我正在进行围棋之旅,我觉得除了并发性之外,我对这门语言有很好的理解。

幻灯片10是一个练习,要求读者并行化一个网络爬虫(并使其不覆盖重复,但我还没有做到。)

以下是我到目前为止的情况:

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

我的问题是,在哪里我把关闭(ch)调用。

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

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

共有3个答案

况博容
2023-03-14

o(1)在地图上查找url的时间,而不是在访问的所有url的切片上查找O(n),这应该有助于最大限度地减少关键部分内花费的时间,对于这个例子来说,这是一个微不足道的时间量,但会与规模相关。

WaitGroup用于阻止顶级Crawl()函数返回,直到所有子go例程完成。

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)
}
令狐跃
2023-03-14

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

// 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)
    }
}
马嘉勋
2023-03-14

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

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的每个实例都有自己的返回通道,调用者函数在其返回通道中收集结果。

 类似资料:
  • 我正在尝试解决围棋练习之旅,读者: 下面是我的解决方案: 它返回,这意味着只有第一个单词lbh penpxrq gur pbqr!是裂开的。我怎样才能破解整个句子?

  • 问题内容: 我正在经历“ A Go of Go”,并且一直在编辑大多数课程,以确保我完全理解它们。我对以下练习的答案有疑问: https : //tour.golang.org/concurrency/10,可在此处找到: https //github.com/golang/tour/blob/master/solutions/ webcrawler.go 我对以下部分有疑问: 从通道添加和删除t

  • 这是一个围棋学习免费软件。 本软件具有人机对局、双人对局、对局演示、对局打谱等功能,可选择2-19路棋盘对局,目前已有三步推算的棋力,可作为围棋初学者的辅助学习工具,也可作为围棋爱好者的辅助研究工具。 本软件采用易语言编写!易语言官方网址: http://www.dywt.com.cn http://www.eyuyan.com 版权所有(C) 2008-2009 梁远海 E-Mail: nply

  • 你正在慢慢地构建我所说的个人流程实践(3P),这根本不是一个新的想法。3P 的目的是客观的洞察如何做事情,而避免杀死你的创造力和生产力。通过简单地跟踪小型指标和制作运行图来指导改进,你可以彻底改变你的工作状况。但是,这样做的风险在于,这会阻碍你快速入侵黑客或完成任务,或者你的 3P 的工作量将比你的实际工作更多。 在我的编程生涯中,我这样做了大约四年,并且它很好地让我认识到我自己和我的工作方式。它

  • 本文向大家介绍开启BootStrap学习之旅,包括了开启BootStrap学习之旅的使用技巧和注意事项,需要的朋友参考一下 本文总结了Bootstrap之所以广泛流传的11大原因。如果你还没有使用Twitter Bootstrap,建议你去了解一下。我也是最近才有所发现的,不过有更好的消息,在前两天微软发布的VS2013正式版中,也已经将BootStrap3.0的版本加入了额,连微软都看到boot

  • 为魅族M8手机开发的围棋打谱软件,还在开发过程中,目前功能不全,但基本功能可用。现在仅支持SGF格式的围棋棋谱。基本功能:打开并解析SGF棋谱,单步向前向后,落子音效,自动提子,显示棋局信息和解说,支持自定义皮肤。