Go 之旅练习#10:爬虫

2024-03-25

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

slide 10 https://tour.golang.org/concurrency/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)
    }
}

我的问题是,我该把close(ch) call.

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

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


看一下并行化部分有效的行动 http://golang.org/doc/effective_go.html#parallel引发解决方案的想法。本质上,您必须关闭该函数的每个返回路径上的通道。实际上,这是 defer 语句的一个很好的用例:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go 之旅练习#10:爬虫 的相关文章

  • 为什么 golang 堆配置文件中的“Total MB”小于顶部的“RES”?

    我有一个用 go 编写的服务 在运行时需要 6 7G 内存 RES 在顶部 所以我使用 pprof 工具试图找出问题所在 go tool pprof pdf http
  • 空或不需要的结构字段

    我有两个结构体 代表将插入到 mongodb 数据库中的模型 一个结构 投资 将另一个结构 集团 作为其字段之一 type Group struct Base Name string json name bson name type Inv
  • 使用私有 git 存储库时的 go 工作区结构

    当你不使用 github 时 我一直在尝试找出 go 代码 工作区的标准文件夹布局 结构 我可以看到如何go get在获取 github 项目时有效 但假设我自己的 git 项目不会有 github com username project
  • 地图中的最大元素数

    GO 中的 Map 最多可以存储多少个元素 如果我需要经常从 Map 访问数据 那么在长时间运行的程序中不断向 Map 添加项目并从中检索项目是一个好主意吗 除了map length类型的最大值之外 map中的元素数量没有理论上的限制 in
  • 将具有联合字段的 C 结构映射到 Go 结构

    我从 Go 中的某些 WinApi 的系统调用中获取结果 我可以轻松地从 C 代码映射简单的结构 但是如何处理如下所示的 C 结构 typedef struct SPC LINK DWORD dwLinkChoice define SPC
  • mysql 查询中的 golang 切片,带有 where in 子句

    我正在运行以下查询 但只获取第一个 id 值 select from table where table id in 1 2 3 4 5 6 7 9 11 13 14 15 17 and table deleted at is null 我
  • Go中funcappend的实现在哪里?

    我对 go 非常感兴趣 并尝试阅读 go 函数的实现 我发现其中一些函数没有实现 如追加或调用 The append built in function appends elements to the end of a slice If i
  • Golang delve,如何启动调试器并启动正在调试的应用程序?

    我正在尝试设置一个可以远程连接的无头深度调试器 我无法找到一种方法来启动调试服务器 而该服务器不会暂停我正在调试的应用程序 我一直在使用dlv attach headless true listen 2345 attach 32但这会暂停该
  • formatFloat :将浮点数转换为字符串[重复]

    这个问题在这里已经有答案了 http golang org pkg strconv http golang org pkg strconv http play golang org p 4VNRgW8WoB http play golang
  • 当所有通道都关闭时中断 select 语句

    我有两个独立生成数据的 goroutine 每个将其发送到一个通道 在我的主 goroutine 中 我想在每个输出进入时使用它们 但不关心它们进入的顺序 每个通道在耗尽其输出时都会自行关闭 虽然 select 语句是像这样独立使用输入的最
  • Go 中的切片分块

    我有一个切片 其中包含约 210 万条日志字符串 我想创建一个切片切片 其中字符串尽可能均匀分布 这是我到目前为止所拥有的 logs is a slice with 2 1 million strings in it var divided
  • 使用 google.protobuf.Timestamp 在 Go 中解析带有时区偏移的日期时间戳

    我正在创建一个将使用 GRPC 和 protobuf 的 Go 应用程序 我的 RPC 服务应获取包含类型的消息google protobuf Timestamp 解析它并最终将其保存在数据库中或对其执行更多操作 我对什么被认为是该类型的有
  • ReverseProxy取决于golang中的request.Body

    我想构建一个 http 反向代理 它检查 HTTP 主体 然后将 HTTP 请求发送到它的上游服务器 你怎么能在 Go 中做到这一点 初始尝试 如下 失败 因为 ReverseProxy 复制传入请求 修改它并发送 但正文已被读取 func
  • Golang标志:忽略丢失的标志并解析多个重复的标志

    我是 Golang 新手 一直无法使用 flag 找到这个问题的解决方案 如何使用 flag 以便我的程序可以处理此类调用 其中 term 标志可能出现可变次数 包括 0 次 myprogram f flag1 myprogram f fl
  • 在 Go 中使用互斥锁

    我想了解互斥体是如何工作的 据我目前的理解 它是为了进行原子操作并同步对某些数据的访问 我在这里构建了一个队列数据结构的示例 https github com arnauddri algorithms blob master data st
  • Golang 优雅地关闭 HTTP 服务器并进行错误处理

    我正在让我的 HTTP 服务器正常关闭 我从帖子中获取了提示here https stackoverflow com questions 39320025 how to stop http listenandserve 并且到目前为止已经像
  • 在 Go 中生成随机、固定长度的字节数组

    我有一个字节数组 固定长度为4 token make byte 4 我需要将每个字节设置为随机字节 我怎样才能以最有效的方式做到这一点 这math rand就我而言 方法不提供随机字节函数 也许有一种内置的方法 或者我应该生成一个随机字符串
  • 当涉及多个渠道时,select 如何工作?

    我发现在多个非缓冲通道上使用 select 时 例如 select case lt chana case lt chanb 即使两个通道都有数据 但在处理此选择时 case chana 和 case chanb 的跟注不平衡 package
  • 为什么奇数的切片容量与偶数的切片行为不同

    我注意到 当容量为奇数时 切片的容量会以不同的方式表现 更具体地说 当向切片添加元素时 切片的容量为doubled当原始容量为偶数时 但当原容量为奇数时 容量为增加一 然后加倍 例子 s make int 28 28 s append s
  • 这两种方式哪一种是惯用的方式? time.Sleep() 还是自动收报机?

    我必须每分钟执行一些语句 我不确定我应该遵循以下哪一项 如果有人能解释内存和 CPU 方面的优缺点 那就太好了 时间 Sleep func main go func for time Sleep time Minute fmt Printl

随机推荐