如何在不关闭无缓冲通道的情况下发现没有接收到任何东西?
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)
}
有没有办法知道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)
}