为什么我把错误推到频道一次,而在golang选择得到错误两次?

rvpgvaaj  于 2022-12-25  发布在  Go
关注(0)|答案(1)|浏览(126)

我是一个golang的新手,我正在学习golang的并发,并尝试写一个简单的爬虫演示,当我读所有给定的网址,我推一个falseprocessChannel,这个推只会执行一次;然后在另一个goroutine中,我选择了processChannel,当得到一个false时,我关闭了应用程序的通道,但是,在这个选择的例子中,我得到了两次false,并且得到了一个panic,表示“panic:关闭关闭的通道”所以,我不明白为什么我按了false一次,但选择了两次false???所有代码如下:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "sync"
    "time"
)

var applicationStatus bool
var urls []string
var urlsProcessed int
var foundUrls []string
var fullText string
var totalURLCount int
var wg sync.WaitGroup

var v1 int

func main() {
    applicationStatus = true
    statusChannel := make(chan int)
    textChannel := make(chan string)
    processChannel := make(chan bool)
    totalURLCount = 0

    urls = append(urls, "https://www.msn.cn/zh-cn/news/other/nasa%E7%AC%AC%E4%BA%94%E6%AC%A1%E8%A7%82%E5%AF%9F%E5%88%B0%E9%BB%91%E6%B4%9E%E5%90%83%E6%8E%89%E4%B8%80%E9%A2%97%E6%B5%81%E6%B5%AA%E7%9A%84%E6%81%92%E6%98%9F/ar-AA15ybhx?cvid=0eaf927e48604c0588413d393c788a8f&ocid=winp2fptaskbarent")
    urls = append(urls, "https://www.msn.cn/zh-cn/news/other/nasa%E7%AC%AC%E4%BA%94%E6%AC%A1%E8%A7%82%E5%AF%9F%E5%88%B0%E9%BB%91%E6%B4%9E%E5%90%83%E6%8E%89%E4%B8%80%E9%A2%97%E6%B5%81%E6%B5%AA%E7%9A%84%E6%81%92%E6%98%9F/ar-AA15ybhx?cvid=0eaf927e48604c0588413d393c788a8f&ocid=winp2fptaskbarent")

    fmt.Println("Starting spider")

    urlsProcessed = 0
    totalURLCount = len(urls)

    go evaluateStatus(statusChannel, processChannel)

    go readURLs(statusChannel, textChannel)

    go appendToFullText(textChannel, processChannel)

    for {
        if applicationStatus == false {
            fmt.Println(fullText)
            fmt.Println("Done!")
            break
        }
        //select {
        //case sC := <-statusChannel:
        //  fmt.Println("Message on statusChannel", sC)
        //}
    }

}

func evaluateStatus(statusChannel chan int, processChannel chan bool) {
    for {
        select {
        case status := <-statusChannel:

            urlsProcessed++

            if status == 0 {
                fmt.Println("got url")
            }

            if status == 1 {
                close(statusChannel)
            }

            if urlsProcessed == totalURLCount {
                fmt.Println("=============>>>>urlsProcessed")
                fmt.Println(urlsProcessed)

                fmt.Println("read all top-level url")
                processChannel <- false
                applicationStatus = false
            }
        }
    }
}

func readURLs(statusChannel chan int, textChannel chan string) {

    time.Sleep(time.Millisecond * 1)

    fmt.Println("grabing ", len(urls), " urls")

    for _, url := range urls {
        resp, _ := http.Get(url)
        text, err := ioutil.ReadAll(resp.Body)

        if err != nil {
            fmt.Println("No HTML body")
        }

        textChannel <- string(text)

        statusChannel <- 0
    }
}

func appendToFullText(textChannel chan string, processChannel chan bool) {
    for {
        select {
        case pC := <-processChannel:
            fmt.Println("pc==============>>>")
            fmt.Println(pC)
            if pC == true {
                // hang out
            }
            if pC == false {
                // all url got
                close(textChannel)
                close(processChannel)
            }
        case tC := <-textChannel:
            fmt.Println("text len: ")
            fmt.Println(len(tC))
            fullText += tC
        }
    }
}

谢谢你的帮助。

0mkxixxg

0mkxixxg1#

根据Go语言规范
关闭通道上的接收操作总是可以立即进行,在接收到任何先前发送的值之后,生成元素类型的零值。
这可以在下面的(playground)演示中看到(注解显示了输出):

func main() {
    processChannel := make(chan bool)

    go func() {
        processChannel <- true
        processChannel <- false
        close(processChannel)
    }()

    fmt.Println(<-processChannel) // true
    fmt.Println(<-processChannel) // false
    fmt.Println(<-processChannel) // false

    select {
    case x := <-processChannel:
        fmt.Println(x) // false
    }
}

在您的代码中,您正在关闭processChannel,以便将来的接收将返回默认值(false)。一个解决方案是在关闭它之后使用processChannel = nil,因为:
空通道永远不会准备好进行通信。
然而,在您的情况下,appendToFullTextpC == false时关闭两个通道;因此,在这样做之后,您可能只需要return(因为两个通道都关闭,保持循环运行是没有意义的)。

  • 请注意,我只扫描了您的代码 *

相关问题