所有工人完成后关闭通道

时间:2018-05-07 14:27:30

标签: go concurrency threadpool goroutine

我正在实现一个网络爬虫,我有一个Parse函数,它将一个链接作为输入,并应该返回页面中包含的所有链接。

我想尽可能快地使用例行程序。为此,我想创建一个工人池。

我设置了一个表示链接links := make(chan string)的字符串通道,并将其作为参数传递给Parse函数。我希望工人通过一个独特的渠道进行沟通。当函数启动时,它会从links获取一个链接,为页面中找到的每个有效链接解析它和**,将链接添加到links

func Parse(links chan string) {
  l := <- links
  // If link already parsed, return
  for url := newUrlFounds {
    links <- url
  }
}

但是,此处的主要问题是指示何时找不到更多链接。我想到的一种方法是在所有工人完成之前等待。但我不知道如何在Go中这样做。

1 个答案:

答案 0 :(得分:1)

正如蒂姆已经评论过的那样,不要使用相同的渠道来阅读和写作工人。这将最终陷入僵局(即使缓冲,因为墨菲)。

更简单的设计就是每个URL只启动一个goroutine。缓冲通道可以作为一个简单的信号量来限制并发解析器的数量(由于它们被阻塞而无法执行任何操作的goroutine通常可以忽略不计)。使用sync.WaitGroup等待所有工作完成。

package main

import (
    "sync"
)

func main() {
    sem := make(chan struct{}, 10) // allow ten concurrent parsers
    wg := &sync.WaitGroup{}

    wg.Add(1)
    Parse("http://example.com", sem, wg)

    wg.Wait()
    // all done
}

func Parse(u string, sem chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()

    sem <- struct{}{}        // grab
    defer func() { <-sem }() // release

    // If URL already parsed, return.

    var newURLs []string

    // ...

    for u := range newURLs {
        wg.Add(1)
        go Parse(u)
    }
}