golang read and write from same channel

huangapple go评论92阅读模式
英文:

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 := &lt;-tasks

		if task.Attemts &gt;= .5 {
			tasks &lt;- task # &lt;- error
		}

		Println(task)
	}
}()

Tasks filling with tasks &lt;- Task{&quot;1&quot;, 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 &lt;- 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 &lt;- task
}()

Or you could have something like this:

requeue = make(chan Task) // could buffer if you want
go func() {
    for {
        tasks &lt;- requeue
    }
}()
for {
    task := &lt;-tasks

	if task.Attemts &gt;= .5 {
		requeue &lt;- 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 &lt;- task
}()

instead of the simple tasks &lt;- task. That will offload the writing to another goroutine, so this one can go right back to reading.

huangapple
  • 本文由 发表于 2017年5月5日 12:26:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/43796510.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定