关于 go:golang 从同一频道读写

golang read and write from same channel

问题代码为:

1
2
3
4
5
6
7
8
9
10
11
12
go func() {
    defer wg.Done()
    for {
        task := <-tasks

        if task.Attemts >= .5 {
            tasks <- task # <- error
        }

        Println(task)
    }
}()

在另一个循环中用 tasks <- Task{"1", rand.Float64()} 填充的任务。

现在我们陷入了僵局……

完整示例:https://play.golang.org/p/s1pnb1Mu_Y

我的代码的重点是 - 创建网络爬虫,它会在失败后尝试解析 url。进行一些尝试,然后删除 url。

可能在 golang 中我们有一些更符合思想的方法来解决这个问题,因为我不知道。


您正在使用非缓冲通道,因此当您尝试使用 tasks <- task 执行发送时,该 goroutine 会坐在那里等待其他内容在通道上读取。由于没有其他内容在频道上读取,因此您会陷入僵局。

使这个特定代码工作的唯一方法是拥有一个完全专用的消费者或使用缓冲通道。即使在此处使用缓冲通道,如果缓冲区在您的单个消费者尝试在其上发送时填满,您也可能会遇到死锁。

如果你真的需要从同一个 goroutine 发送,你将不得不生成一个新的 goroutine 来发送它。类似于

的东西

1
2
3
go func() {
    tasks <- task
}()

或者你可以有这样的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
requeue = make(chan Task) // could buffer if you want
go func() {
    for {
        tasks <- requeue
    }
}()
for {
    task := <-tasks

    if task.Attemts >= .5 {
        requeue <- task
    }

    Println(task)
}

当然要处理该通道的关闭等。


如果这是从通道读取的唯一 goroutine,它也不能写入它。

如果您使用缓冲通道,它可能会工作一点点,但这只会将您的问题推回原处。你真正想做的可能是使用

1
2
3
go func(){
   tasks <- task
}()

而不是简单的 tasks <- task。这会将写入卸载到另一个 goroutine,所以这个可以直接回到阅读。