如何在不关闭无缓冲通道的情况下发现没有接收到任何东西?

How to find out nothing is being received in an unbuffered channel without closing it?

有没有办法知道channel中的值是否都被消耗了?我正在制作一个从种子网站递归获取网站的爬虫。我没有关闭频道,因为它从服务器消耗并且每次发送新站点时都应该抓取。对于给定的种子站点,除了超时之外,我找不到更好的方法来了解子任务的完成情况。如果有办法知道 channel 中没有值(剩下的被消耗),我的程序可以退出子任务并继续监听服务器。

没有这样的东西 "queued in an unbuffered channel." 如果通道没有缓冲,根据定义它总是空的。如果它被缓冲,那么它可能有一定数量的元素到它的大小。但是试图读取其中有多少元素总是会导致竞争条件,所以不要那样设计(在 Go 中也是不可能的)。

理想情况下,避免需要知道 children 何时完成的设计,但当你必须时,给他们发送一个频道来回复你。当他们回应时,您就知道他们已经完成了。

您描述的问题类型在 Go 博客和讲座中有详细介绍:

您可以通过在 select 语句中使用 default 来确定 goroutine 是否在通道的另一端被阻塞。例如:

package main

import (
    "fmt"
    "time"
)

var c = make(chan int)

func produce(i int) {
    c <- i
}

func consume() {
    for {
        select {
        case i := <-c:
            fmt.Println(i)
        default:
            return
        }
    }
}

func main() {
    for i := 0; i < 10; i++ {
        go produce(i)
    }
    time.Sleep(time.Millisecond)
    consume()
}

请记住,这不是队列。如果你有 1 个生产 goroutine 循环并在发送一个值和再次返回循环之间产生多个值,default 情况就会发生,你的消费者将继续前进。

您可以使用超时:

case <-time.After(time.Second):

这会给您的生产者一秒钟的时间来产生另一个值,但您最好还是使用终端值。将您要发送的任何内容包装在一个结构中:

type message struct {
    err error
    data theOriginalType
}

然后发送那个东西。然后使用 io.EOF 或自定义错误 var Done = errors.New("DONE") 来表示完成。

既然你有递归问题为什么不用WaitGroup?每次启动新任务时增加等待组,每次任务完成时减少等待组。然后有一个外部任务等待完成。例如,这是计算斐波那契数的一种非常低效的方法:

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func fib(c chan int, n int) {
    defer wg.Done()
    if n < 2 {
        c <- n
    } else {
        wg.Add(2)
        go fib(c, n - 1)
        go fib(c, n - 2)
    }
}

func main() {
    wg.Add(1)
    c := make(chan int)
    go fib(c, 18)
    go func() {
        wg.Wait()
        close(c)
    }()
    sum := 0
    for i := range c {
        sum += i
    }
    fmt.Println(sum)
}