下面这段代码来自《 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 这个操作加入专有协程就有引起死锁呢?
1
iceheart 2021-04-02 09:20:03 +08:00 via Android
就是单一协程程服务的模式代替临界区访问的模式
|
2
ihipop 2021-04-02 09:28:18 +08:00 via Android
一楼说的高大上,单纯从代码看,中文注释处这是把结果发送给 work list,但是 worklist 可能是满的,如果读取 work list 的那边读取慢或者不读取,爬取协程就被阻塞在写入 work list chan 那边了,把发送行为放到子协程就不影响爬取协程继续爬取。
|
3
ihipop 2021-04-02 09:36:33 +08:00 via Android
@ihipop 然后
main 函数末尾会把所有未爬取过的连接过滤一遍,再发给 unseenlinks,如果直接把 foundlinks 发现的链接直接给 crawl 爬取,那么这里可能存在重复的链接,爬虫就会陷入死循环了。 |
4
yuantingwuji OP @ihipop 噢,我好像明白了,如果 worklist <- foundLinks 操作不加入专有协程,则会阻塞 unseenLinks 这个变量的读取,这样会进一步阻塞主协程 unseenLinks 的接收操作,进而阻塞主协程 worklist 的的读取,这样就形成死锁了。
|
5
yeqown 2021-04-02 09:53:08 +08:00
unseenLinks 和 worklist 在阻塞情况下互相等待,就会出现死锁
|
6
mogg 2021-04-02 10:52:46 +08:00
考虑 unseenLinks 和 worklist 都满了的情况,worklist <- foundLinks, unseenLinks <- link 都阻塞,这时候就死锁了
|
7
treblex 2021-04-02 11:21:16 +08:00
跑个题,为啥接收命令行参数也要开个协程
|
8
VincentYe123 2021-04-02 11:40:58 +08:00 1
@treblex 和上面的回答的原因一样
|
10
hq136234303 2021-04-02 14:34:41 +08:00
@ihipop 这样回照成携程的无限上涨
|
11
renoyuan 2021-04-09 14:33:00 +08:00
《 GO 语言圣经》这本书中文版有有一种偷工减料的感觉。是我找的版本不对吗: https://books.studygolang.com/gopl-zh/
|