当一个从未接触过多线程程序的 PHP 开发人员开始学习 golang 和 channel 时,可能会发生这种情况。
我正在进行围棋之旅的最后一个练习,[Exercise: Web Crawler] (在此之前,我对其他练习没有任何问题)
虽然我正在尝试编写尽可能简单的代码, 我的 Crawl 方法如下所示:
func Crawl(url string, depth int, fetcher Fetcher) {
// kick off crawling by passing initial Url to a Job queue
Queue <- Job{
url,
depth,
}
// make sure we close the Queue channel
defer close(Queue)
// read from the Queue
for job := range Queue {
// if fetched or has hit the bottom of depth,
// just continue right away to pick up next Job
if fetched.Has(job.Url) || job.Depth <= 0 {
continue
}
fres := fetcher.Fetch(job.Url)
fetched.Add(job.Url, fres)
for i := range fres.Urls {
// send new urls just fetched from current url in Job
// to the Queue
Queue <- Job{
fres.Urls[i], job.Depth - 1,
}
}
}
for _, res := range fetched.m {
fmt.Println(res)
}
}
go run 说我不应该写任何 go 代码然后返回 PHP:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.Crawl(0xf37c1, 0x12, 0x4, 0x1600e0, 0x104401c0, 0x104000f0)
/tmp/sandbox452918312/main.go:64 +0x80
main.main()
/tmp/sandbox452918312/main.go:87 +0x60
当然,我用谷歌搜索了这个问题,结论通常是:“关闭你的 channel ”,我这样做了(是吗?)。
那么,有人可以指出我在这里遗漏了什么吗?
完整代码在这里:https://play.golang.org/p/-98SdVndD6
这个练习最惯用的 golang 方法是什么?我找到了一些。
等哪个对您来说似乎是一个干净的解决方案?
此外,我是否应该仅与 goroutines 一起使用 channel ?
请您参考如下方法:
您正在“推迟”队列的关闭。这意味着“当这个函数(抓取)退出时关闭队列!”
然后您进入一个循环,该循环将阻塞直到它:
- 收到一件元素或
- “队列”已关闭
开始时队列中添加了一个'Job'(这将允许循环运行一次),然后在第一次运行结束时,循环将阻塞,直到满足上述两个条件之一又见面了。
注意:运行第一个循环可能会向队列中添加更多项目(因此会导致更多迭代),但在某些时候,循环的队列将耗尽并且循环将再次阻塞等待以上两种情况之一
但是,永远不会再有任何项目添加到队列中(因此 #1 失败)并且“队列”仅在此函数退出后关闭,这在循环退出之前不会发生(因此 #2 失败)。
TLDR:您的循环正在等待您的函数退出,而您的函数正在等待您的循环退出 - 死锁






