V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yuantingwuji
V2EX  ›  Go 编程语言

求问《GO 语言圣经》里这句话应该怎么解释?

  •  
  •   yuantingwuji · 2021-04-02 09:10:34 +08:00 · 3556 次点击
    这是一个创建于 1329 天前的主题,其中的信息可能已经有所发展或是发生改变。

    下面这段代码来自《 GO 语言圣经》 8.6. 示例: 并发的 Web 爬虫

    func main() {
        worklist := make(chan []string)  // lists of URLs, may have duplicates
        unseenLinks := make(chan string) // de-duplicated URLs
    
        // Add command-line arguments to worklist.
        go func() { worklist <- os.Args[1:] }()
    
        // Create 20 crawler goroutines to fetch each unseen link.
        for i := 0; i < 20; i++ {
            go func() {
                for link := range unseenLinks {
                    foundLinks := crawl(link)
                    go func() { worklist <- foundLinks }() //避免死锁
                }
            }()
        }
    
        // The main goroutine de-duplicates worklist items
        // and sends the unseen ones to the crawlers.
        seen := make(map[string]bool)
        for list := range worklist {
            for _, link := range list {
                if !seen[link] {
                    seen[link] = true
                    unseenLinks <- link
                }
            }
        }
    }
    

    书里面说,“crawl 函数爬到的链接在一个专有的 goroutine 中被发送到 worklist 中来避免死锁”,就是上面代码中加了中文注释那一行,为什么不把 worklist <- foundLinks 这个操作加入专有协程就有引起死锁呢?

    11 条回复    2021-04-09 14:33:00 +08:00
    iceheart
        1
    iceheart  
       2021-04-02 09:20:03 +08:00 via Android
    就是单一协程程服务的模式代替临界区访问的模式
    ihipop
        2
    ihipop  
       2021-04-02 09:28:18 +08:00 via Android
    一楼说的高大上,单纯从代码看,中文注释处这是把结果发送给 work list,但是 worklist 可能是满的,如果读取 work list 的那边读取慢或者不读取,爬取协程就被阻塞在写入 work list chan 那边了,把发送行为放到子协程就不影响爬取协程继续爬取。
    ihipop
        3
    ihipop  
       2021-04-02 09:36:33 +08:00 via Android
    @ihipop 然后
    main 函数末尾会把所有未爬取过的连接过滤一遍,再发给 unseenlinks,如果直接把 foundlinks 发现的链接直接给 crawl 爬取,那么这里可能存在重复的链接,爬虫就会陷入死循环了。
    yuantingwuji
        4
    yuantingwuji  
    OP
       2021-04-02 09:45:58 +08:00
    @ihipop 噢,我好像明白了,如果 worklist <- foundLinks 操作不加入专有协程,则会阻塞 unseenLinks 这个变量的读取,这样会进一步阻塞主协程 unseenLinks 的接收操作,进而阻塞主协程 worklist 的的读取,这样就形成死锁了。
    yeqown
        5
    yeqown  
       2021-04-02 09:53:08 +08:00
    unseenLinks 和 worklist 在阻塞情况下互相等待,就会出现死锁
    mogg
        6
    mogg  
       2021-04-02 10:52:46 +08:00
    考虑 unseenLinks 和 worklist 都满了的情况,worklist <- foundLinks, unseenLinks <- link 都阻塞,这时候就死锁了
    treblex
        7
    treblex  
       2021-04-02 11:21:16 +08:00
    跑个题,为啥接收命令行参数也要开个协程
    VincentYe123
        8
    VincentYe123  
       2021-04-02 11:40:58 +08:00   ❤️ 1
    @treblex 和上面的回答的原因一样
    kkbblzq
        9
    kkbblzq  
       2021-04-02 12:21:38 +08:00   ❤️ 1
    @treblex 因为是无缓冲区的 chan,原因还是和上面一样
    hq136234303
        10
    hq136234303  
       2021-04-02 14:34:41 +08:00
    @ihipop 这样回照成携程的无限上涨
    renoyuan
        11
    renoyuan  
       2021-04-09 14:33:00 +08:00
    《 GO 语言圣经》这本书中文版有有一种偷工减料的感觉。是我找的版本不对吗: https://books.studygolang.com/gopl-zh/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4473 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 38ms · UTC 01:05 · PVG 09:05 · LAX 17:05 · JFK 20:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.