在Go语言中,对带缓冲的通道进行范围遍历时会发生阻塞。

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

Range over buffered channel in Go is blocking

问题

我在迭代缓冲通道时遇到了阻塞的问题。这段代码中,Never called 永远不会被调用,这是为什么呢?这证明了两个来源填充了通道(通道的最大容量为2)。我错过了什么吗?

编辑后的代码使用了 sync.WaitGroup 来解决这个问题。它会等待所有的 goroutine 完成后再关闭通道。在每个 goroutine 完成后,它会调用 wg.Done() 来通知 WaitGroup,表示该 goroutine 已经完成。然后,通过关闭通道来结束循环,读取通道中的结果并将其附加到 results 切片中。最后,将 results 返回给客户端。

希望这能帮到你!

英文:

I must be having a brain block here, but I am getting blocked when iterating over a buffered channel

	results := []search.Book{}
	resultsStream := make(chan []search.Book, 2)
	defer close(resultsStream)

	// parallelize searches to optimize response time
	for _, src := range sources {
		go src.Search(bookName, resultsStream)
	}

	counter := 0
	for sourceResults := range resultsStream {
		counter = counter + 1
		results = append(results, sourceResults...)

		fmt.Println(counter)
	}

	fmt.Println("Never called")

Output

1
2

This proves the 2 sources populate the channel (which is the max capacity).
What am I missing here? Never called is, well, never called.

Edit

	var wg sync.WaitGroup
	results := []search.Book{}
	resultsStream := make(chan []search.Book, len(sources))
	defer close(resultsStream)

	// parallelize searches to optimize response time
	for _, src := range sources {
        wg.Add(1)
		go src.Search(bookName, resultsStream, &wg)
	}

	wg.Wait()
	close(resultsStream)
	for sourceResults := range resultsStream {
		results = append(results, sourceResults...)
	}

	c.JSON(http.StatusOK, gin.H{
		"results": results,
	})

答案1

得分: 2

当你在Go语言中使用range遍历一个通道时,只有在你使用break跳出循环或者通过close(resultsStream)关闭通道时,for循环才会退出。

英文:

When you range over a go channel the for loop is not exited until you either break out of it or the channel is closed using close(resultsStream)

答案2

得分: 2

循环for sourceResults := range resultsStream会重复从通道接收值,直到通道被关闭。一旦发送方完成并关闭通道,循环将结束。

你可以为每个并行搜索创建一个新的通道,一旦所有工作协程完成,你可以关闭该通道。这将结束接收方的循环(注意:不要从接收方关闭通道,因为发送方不会知道,向已关闭的通道发送数据会引发恐慌)。

英文:

The loop for sourceResults := range resultsStream receives values from the channel repeatedly until it is closed. Once sender is done and closes the channel loop will end.

You can create new channel for each parallel search and once all worker goroutines finish you can close that channel. That will end the receiver loop (NOTE: don't close channel from the receiver side as senders won't know and sending to a closed channel causes panic).

huangapple
  • 本文由 发表于 2022年12月21日 09:36:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/74870553.html
匿名

发表评论

匿名网友

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

确定