英文:
golang read and write from same channel
问题
问题代码如下:
go func() {
defer wg.Done()
for {
task := <-tasks
if task.Attemts >= .5 {
tasks <- task // <- error
}
Println(task)
}
}()
在另一个循环中,使用 tasks <- Task{"1", rand.Float64()}
来填充 tasks
。
现在我们遇到了死锁问题...
完整示例:https://play.golang.org/p/s1pnb1Mu_Y
我的代码目标是创建一个网络爬虫,它在失败后尝试解析URL。尝试几次后放弃URL。
也许在Go语言中,我们有一种更符合惯例的方法来解决这个问题,因为我不知道。
英文:
The problem code is:
go func() {
defer wg.Done()
for {
task := <-tasks
if task.Attemts >= .5 {
tasks <- task # <- error
}
Println(task)
}
}()
Tasks filling with tasks <- Task{"1", rand.Float64()}
in another loop.
And now we've got deadlock...
Full example: https://play.golang.org/p/s1pnb1Mu_Y
The point of my code is - create web scraper , which one will try to parse urls after fails. Take some attempts and then drop url.
Might be in golang we have some more ideomatic way to solve this problem, cuz i don't know.
答案1
得分: 2
您正在使用非缓冲通道,因此当您尝试使用 tasks <- task
发送数据时,该goroutine的执行会停在那里,等待其他地方对通道进行读取。由于没有其他地方在读取该通道,所以会发生死锁。
要使这段特定的代码工作,唯一的方法是要么有一个完全专用的消费者,要么使用带缓冲的通道。即使在这里使用了带缓冲的通道,如果在单个消费者尝试发送数据时缓冲区已满,仍然可能发生死锁。
如果您真的需要在同一个goroutine中发送数据,您将不得不启动一个新的goroutine来发送它。可以按照以下方式进行操作:
go func() {
tasks <- task
}()
或者您可以使用以下代码:
requeue = make(chan Task) // 如果需要,可以使用缓冲区
go func() {
for {
tasks <- requeue
}
}()
for {
task := <-tasks
if task.Attempts >= .5 {
requeue <- task
}
Println(task)
}
当然,还需要处理该通道的关闭等操作。
英文:
You are using a nonbuffered channel so when you try to send with tasks <- task
execution in that goroutine sits there waiting for something else to read on the channel. Since nothing else is reading on the channel you get a deadlock.
The only way to make this specific code work is to have a fully dedicated consumer or use a buffered channel. Even with a buffered channel here you could get a deadlock if the buffer fills up at the point your single consumer tries to send on it.
If you really need to send from that same goroutine you're going to have to spawn a new goroutine just to send it. Something along the lines of
go func() {
tasks <- task
}()
Or you could have something like this:
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)
}
Handling the closing of that channel and the like of course.
答案2
得分: 0
如果这是唯一一个从通道中读取的 goroutine,它就不能同时向其写入。
如果你使用了一个带缓冲的通道,可能会在一段时间内有点工作,但这只是把问题推迟了。你真正想做的可能是使用:
go func(){
tasks <- task
}()
而不是简单的 tasks <- task
。这将把写入操作转移到另一个 goroutine 中,这样当前的 goroutine 就可以立即返回到读取操作。
英文:
If this is the only goroutine reading from the channel, it cannot also write to it.
It is possible if you used a buffered channel, it would sort of work for a little bit, but that would only push your problem back. What you really want to do is likely use
go func(){
tasks <- task
}()
instead of the simple tasks <- task
. That will offload the writing to another goroutine, so this one can go right back to reading.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论