【问题标题】:Closing channel when all workers have finished所有工作人员完成后关闭通道
【发布时间】:2018-10-17 09:37:58
【问题描述】:

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

我想充分利用 goroutine 使其尽可能快。为此,我想创建一个工人池。

我设置了一个表示链接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 中这样做。

【问题讨论】:

  • 我不确定您的频道设计是否最佳。例如,如果links 处于无缓冲状态,并且只有一个工作程序 go 例程在运行 Parse,那么当找到新 URL 时,您会遇到死锁。

标签: go concurrency threadpool goroutine


【解决方案1】:

正如 Tim 已经评论的那样,不要在工作人员中使用相同的渠道进行阅读和写作。这最终会死锁(即使缓冲了,因为墨菲)。

一个更简单的设计是简单地为每个 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)
    }
}

【讨论】:

    猜你喜欢
    • 2020-01-04
    • 1970-01-01
    • 1970-01-01
    • 2018-04-09
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2012-08-11
    • 1970-01-01
    相关资源
    最近更新 更多