golang:带有select的goroute不会停止,除非我添加了fmt.Print()

尝试了71号Go Tour练习

如果运行像go run 71_hang.go ok,则可以正常运行。

但是,如果使用go run 71_hang.go nogood,它将永远运行。

唯一的区别是额外fmt.Print("")defaultselect语句。

我不确定,但是我怀疑某种无限循环和竞争条件吗?这是我的解决方案。

注意:这不是死锁,因为Go没死 throw: all goroutines are asleep - deadlock!

package main


import (

    "fmt"

    "os"

)


type Fetcher interface {

    // Fetch returns the body of URL and

    // a slice of URLs found on that page.

    Fetch(url string) (body string, urls []string, err error)

}


func crawl(todo Todo, fetcher Fetcher,

    todoList chan Todo, done chan bool) {

    body, urls, err := fetcher.Fetch(todo.url)

    if err != nil {

        fmt.Println(err)

    } else {

        fmt.Printf("found: %s %q\n", todo.url, body)

        for _, u := range urls {

            todoList <- Todo{u, todo.depth - 1}

        }

    }

    done <- true

    return

}


type Todo struct {

    url   string

    depth int

}


// Crawl uses fetcher to recursively crawl

// pages starting with url, to a maximum of depth.

func Crawl(url string, depth int, fetcher Fetcher) {

    visited := make(map[string]bool)

    doneCrawling := make(chan bool, 100)

    toDoList := make(chan Todo, 100)

    toDoList <- Todo{url, depth}


    crawling := 0

    for {

        select {

        case todo := <-toDoList:

            if todo.depth > 0 && !visited[todo.url] {

                crawling++

                visited[todo.url] = true

                go crawl(todo, fetcher, toDoList, doneCrawling)

            }

        case <-doneCrawling:

            crawling--

        default:

            if os.Args[1]=="ok" {   // *

                fmt.Print("")

            }

            if crawling == 0 {

                goto END

            }

        }

    }

END:

    return

}


func main() {

    Crawl("http://golang.org/", 4, fetcher)

}


// fakeFetcher is Fetcher that returns canned results.

type fakeFetcher map[string]*fakeResult


type fakeResult struct {

    body string

    urls []string

}


慕仙森
浏览 223回答 2
2回答

30秒到达战场

default在选择select更改的方式中添加一条语句。如果没有默认语句,则select将阻止等待通道上的任何消息。使用默认语句时,select将在每次从通道中读取无内容时运行默认语句。在您的代码中,我认为这会造成无限循环。把fmt.Print在声明中允许调度安排其它够程。如果您像这样更改代码,则它可以正常工作,并以非阻塞方式使用select,这将允许其他goroutines正常运行。&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case todo := <-toDoList:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if todo.depth > 0 && !visited[todo.url] {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; crawling++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; visited[todo.url] = true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go crawl(todo, fetcher, toDoList, doneCrawling)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; case <-doneCrawling:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; crawling--&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if crawling == 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }如果使用GOMAXPROCS = 2,则可以使原始代码正常工作,这是调度程序在无限循环中忙碌的另一个提示。请注意,goroutine是合作安排的。我对您的问题不完全了解,这select是goroutine应该产生的点-我希望其他人可以解释为什么您的示例中没有它。

ibeautiful

您拥有100%的CPU负载,因为几乎所有情况下都会执行默认情况,这会导致无限循环,因为它会一遍又一遍地执行。在这种情况下,Go调度程序在设计上不会将控制权交给另一个goroutine。因此,任何其他goroutine将永远没有机会进行设置,crawling != 0而您将遇到无限循环。我认为,如果要使用select语句,则应删除默认情况,而应创建另一个通道。否则,运行时程序包将帮助您走脏路:runtime.GOMAXPROCS(2)&nbsp;将起作用(或导出GOMAXPROCS = 2),这样您将拥有多个OS线程执行runtime.Gosched()不时致电内部抓取。即使CPU负载为100%,这也将明确地将控制权传递给另一个Goroutine。编辑:是的,以及fmt.Printf之所以有所作为的原因:因为它明确地将控制权传递给某些syscall东西...;)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go