了解golang频道。所有goroutines都睡着了 - 僵局[Tour of Go,Crawler]

时间:2017-05-03 00:57:46

标签: go concurrency channel

当从未处理多线程程序的PHP开发人员开始学习golang和渠道时,可能会发生这种情况。

我参加了Tour of Go的最后一次练习,[Exercise: Web Crawler](在此之前我没有遇到其他练习的问题)

我尝试编写尽可能简单的代码, 我的抓取方法如下所示:

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

当然,我已经搜索了这个问题,结论通常是:&#34;关闭你的chans&#34;,我做了(我做过吗?)。

那么,有人可以指出我在这里缺少什么吗?

完整的代码在这里:https://play.golang.org/p/-98SdVndD6

这项运动最常用的golang方式是什么?我找到了一把。

等。哪一个似乎是一个干净的解决方案?

另外,我应该在goroutines中使用频道吗?

1 个答案:

答案 0 :(得分:1)

您推迟关闭队列。这意味着&#34;当此功能(抓取)退出时关闭队列!&#34;

然后你输入一个阻塞的循环,直到它:

  1. 收到项目或
  2. &#39;队列&#39;已关闭
  3. 有一个&#39; Job&#39;在开始时添加到队列中(这将允许循环运行一次),然后在第一次运行结束时,循环将阻塞,直到再次满足上述两个条件中的任何一个。

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

    但是,队列中永远不会再添加任何项目(因此#1失败)和队列&#39;只有在此函数退出后才会关闭,直到循环退出后才会发生(因此#2失败)。

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