繁体   English   中英

为什么我将 false 推送到频道一次,但在 golang 中选择了两次错误?

[英]why I push false to channel once, but select got false twice in golang?

我是golang的新手,正在研究golang的并发,并尝试写了一个简单的爬虫demo,当我读取所有给定的url时,我向processChannel推送一个false ,这个推送只会执行一次; 然后在其他 goroutine 中,我在processChannel上选择,当得到一个false时,我关闭了申请通道,但是,在这个选择的情况下,我得到了两次 false,并且因为“恐慌:关闭关闭的通道”而感到恐慌所以,我不能明白为什么我推了一次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
        }
    }
}

感谢您的帮助。

根据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因为

一个 nil 通道永远不会准备好进行通信。

但是,在您的情况下, appendToFullTextpC == false时关闭两个通道; 因此,您可能应该在这样做之后return (因为两个通道都关闭了,所以保持循环运行没有意义)。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM