当一个从未接触过多线程程序的 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 ?

请您参考如下方法:

您正在“推迟”队列的关闭。这意味着“当这个函数(抓取)退出时关闭队列!”

然后您进入一个循环,该循环将阻塞直到它:

  1. 收到一件元素或
  2. “队列”已关闭

开始时队列中添加了一个'Job'(这将允许循环运行一次),然后在第一次运行结束时,循环将阻塞,直到满足上述两个条件之一又见面了。

注意:运行第一个循环可能会向队列中添加更多项目(因此会导致更多迭代),但在某些时候,循环的队列将耗尽并且循环将再次阻塞等待以上两种情况之一

但是,永远不会再有任何项目添加到队列中(因此 #1 失败)并且“队列”仅在此函数退出后关闭,这在循环退出之前不会发生(因此 #2 失败)。

TLDR:您的循环正在等待您的函数退出,而您的函数正在等待您的循环退出 - 死锁


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!