英文:
go concurrency all goroutines are asleep - deadlock
问题
抱歉,我只能为您提供翻译服务,无法执行代码。以下是您提供的代码的翻译:
对不起,这可能是一个初学者的问题,但我对Go语言中的并发部分感到困惑。下面的程序是我正在编写的一个较大程序的简化版本,因此我希望保持结构与下面的类似。
基本上,我想使用无缓冲通道并发地运行addCount(..)
,而不是等待4秒钟,当int_slice
中的所有元素都被处理完毕后,我想对它们进行另一种操作。然而,这个程序以一个"panic: close of closed channel"的错误结束,如果我移除通道的关闭操作,我会得到我期望的输出,但它会出现"fatal error: all goroutines are asleep - deadlock"的错误。
在这种情况下,我该如何正确实现并发部分?
提前感谢您的帮助!
package main
import (
"fmt"
"time"
)
func addCount(num int, counter chan<- int) {
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
counter := make(chan int)
int_slice := []int{2, 4}
for _, item := range int_slice {
go addCount(item, counter)
close(counter)
}
for item := range counter {
fmt.Println(item)
}
}
英文:
Sorry about the noob question but I'm having a hard time wrapping my head around the concurrency part of go. Basically this program below is a simplified version of a larger one I'm writing, thus I want to keep the structure similar to below.
Basically instead of waiting 4 seconds I want to run addCount(..)
concurrent using the unbuffered channel and when all elements in the int_slice
has been processed I want to do another operation on them. However this program ends with a "panic: close of closed channel" and if I remove the closing of the channel I'm getting the output I'm expecting but it panics with: "fatal error: all goroutines are asleep - deadlock"
How can I implement the concurrency part correctly in this scenario?
Thanks in advance!
package main
import (
"fmt"
"time"
)
func addCount(num int, counter chan<- int) {
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
counter := make(chan int)
int_slice := []int{2, 4}
for _, item := range int_slice {
go addCount(item, counter)
close(counter)
}
for item := range counter {
fmt.Println(item)
}
}
答案1
得分: 5
以下是我在代码中发现的问题,并基于你的实现给出的一个可工作版本。
-
如果一个goroutine尝试向一个"无缓冲"通道写入数据,它将被阻塞,直到有其他goroutine从该通道读取数据。由于你在它们完成向通道写入数据之前没有进行读取,所以在这里出现了死锁。
-
在它们被阻塞时关闭通道会打破死锁,但会导致错误,因为它们现在无法向已关闭的通道写入数据。
解决方案包括:
-
创建一个带缓冲的通道,以便它们可以无阻塞地写入数据。
-
使用
sync.WaitGroup
,以便在关闭通道之前等待所有goroutine完成。 -
在所有操作完成后,从通道中读取数据。
请参考以下代码(带有注释):
package main
import (
"fmt"
"time"
"sync"
)
func addCount(num int, counter chan<- int, wg *sync.WaitGroup) {
// 从同步组中移除一个
defer wg.Done()
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
int_slice := []int{2, 4}
// 使用切片的大小创建带缓冲的通道,以便它们可以无阻塞地写入数据
counter := make(chan int, len(int_slice))
var wg sync.WaitGroup
for _, item := range int_slice {
// 向同步组中添加一个,表示我们需要等待一个更多的goroutine
wg.Add(1)
go addCount(item, counter, &wg)
}
// 等待所有goroutine结束
wg.Wait()
// 关闭通道,不再期望有数据写入
close(counter)
// 读取通道中剩余的值
for item := range counter {
fmt.Println(item)
}
}
希望对你有帮助!
英文:
Here are the issues I spotted in the code, and below a working version based on your implementation.
-
If a goroutine tries to write to an "unbuffered" channel, it will block until someone reads from it. Since you are not reading until they finish writing to the channel, you have a deadlock there.
-
Closing the channel while they are blocked breaks the deadlock, but gives an error since they now can't write to a closed channel.
Solution involves:
-
Creating a buffered channel so that they can write without blocking.
-
Using a
sync.WaitGroup
so that you wait for the goroutines to finish before closing the channel. -
Reading from the channel at the end, when all is done.
See here, with comments:
package main
import (
"fmt"
"time"
"sync"
)
func addCount(num int, counter chan<- int, wg *sync.WaitGroup) {
// clear one from the sync group
defer wg.Done()
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
int_slice := []int{2, 4}
// make the slice buffered using the slice size, so that they can write without blocking
counter := make(chan int, len(int_slice))
var wg sync.WaitGroup
for _, item := range int_slice {
// add one to the sync group, to mark we should wait for one more
wg.Add(1)
go addCount(item, counter, &wg)
}
// wait for all goroutines to end
wg.Wait()
// close the channel so that we not longer expect writes to it
close(counter)
// read remaining values in the channel
for item := range counter {
fmt.Println(item)
}
}
答案2
得分: 0
为了举例,这里是 @eugenioy 提交的稍作修改的版本。它允许使用无缓冲通道,并在值到达时立即读取,而不是像常规的 for 循环那样在最后读取。
package main
import (
"fmt"
"sync"
"time"
)
func addCount(num int, counter chan<- int, wg *sync.WaitGroup) {
// 从同步组中清除一个
defer wg.Done()
// 不需要,除非你想要减慢输出速度
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
// Go 中变量名不使用下划线
intSlice := []int{2, 4}
counter := make(chan int)
var wg sync.WaitGroup
for _, item := range intSlice {
// 将同步组计数加一,表示我们需要等待一个更多的 goroutine
wg.Add(1)
go addCount(item, counter, &wg)
}
// 通过将等待和关闭操作放在一个 goroutine 中,我可以在它完成之前开始读取通道,而且我也不需要知道切片的大小
go func() {
wg.Wait()
close(counter)
}()
for item := range counter {
fmt.Println(item)
}
}
英文:
For the sake of having examples here's a slightly modified version of what @eugenioy submitted. It allows for the use of an unbuffered channel and the reading of values as they come in instead of at the end like a regular for loop.
package main
import (
"fmt"
"sync"
"time"
)
func addCount(num int, counter chan<- int, wg *sync.WaitGroup) {
// clear one from the sync group
defer wg.Done()
// not needed, unless you wanted to slow down the output
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
// variable names don't have underscores in Go
intSlice := []int{2, 4}
counter := make(chan int)
var wg sync.WaitGroup
for _, item := range intSlice {
// add one to the sync group, to mark we should wait for one more
wg.Add(1)
go addCount(item, counter, &wg)
}
// by wrapping wait and close in a go routine I can start reading the channel before its done, I also don't need to know the size of the
// slice
go func() {
wg.Wait()
close(counter)
}()
for item := range counter {
fmt.Println(item)
}
}
答案3
得分: -2
package main
import (
"fmt"
"time"
)
func addCount(num int, counter chan<- int) {
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
counter := make(chan int)
int_slice := []int{2, 4}
for _, item := range int_slice {
go addCount(item, counter)
fmt.Println(<-counter)
}
}
package main
import (
"fmt"
"time"
)
func addCount(num int, counter chan<- int) {
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
counter := make(chan int)
int_slice := []int{2, 4}
for _, item := range int_slice {
go addCount(item, counter)
fmt.Println(<-counter)
}
}
英文:
package main
import (
"fmt"
"time"
)
func addCount(num int, counter chan <- int) {
time.Sleep(time.Second * 2)
counter <- num * 2
}
func main() {
counter := make(chan int)
int_slice := []int{2, 4}
for _, item := range int_slice {
go addCount(item, counter)
fmt.Println(<-counter)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论