Concurrency Enque和deque使通道死锁

Concurrency Enque和deque使通道死锁,concurrency,go,deadlock,race-condition,goroutine,Concurrency,Go,Deadlock,Race Condition,Goroutine,我尝试在单个通道中实现队列、出列和重新排队 我有两个问题: 我为什么会陷入僵局?我期待一个无限循环(因为我正在重新计算生成更多元素的元素的数量,等等)。范围队列是否应该始终侦听频道 这是deadloks前打印的一部分: 1000排队 排队1001 出列1001 出列1001 使用1001 使用1001 两个不同的goroutine是否对同一个元素进行出列?我不明白为什么这场数据竞赛;我认为范围每次都会选择一个 func main(){ 队列:=make(chan int) 起点:=10 g

我尝试在单个通道中实现队列、出列和重新排队

我有两个问题:

  • 我为什么会陷入僵局?我期待一个无限循环(因为我正在重新计算生成更多元素的元素的数量,等等)。
    范围队列是否应该始终侦听频道

  • 这是deadloks前打印的一部分:

    1000排队
    排队1001
    出列1001
    出列1001
    使用1001
    使用1001

两个不同的goroutine是否对同一个元素进行出列?我不明白为什么这场数据竞赛;我认为
范围每次都会选择一个

func main(){
队列:=make(chan int)
起点:=10

go func(){queue这是一个作用域问题。在开始使用
go func(){…}
时,经常会发生这种情况;这可能比并发性的实际问题更常见。本文中有相关章节

您创建的匿名
func
获取对外部作用域中变量的引用,而不是对
func
语句运行时的值的引用。并且循环变量在每次通过循环时都会更新,因此它会在
go
语句和goroutine实际运行时发生变化

您可以通过在循环的每次迭代期间声明另一个变量来解决此问题(即,在
for
大括号内)。如果您的循环是
for i:=range arr{…}
您可以只添加
i:=i
。因此,此错误版本:

arr := make([]int, 10)
for i := range arr {
    go func() { 
        fmt.Println(i)
    }()
}
..固定版本,在循环内重新定义
i

arr := make([]int, 10)
for i := range arr {
    i := i
    go func() { 
        fmt.Println(i)
    }()
}
..重新声明
i
的另一种可能更优雅的方式是将其作为匿名
func
的参数;这样它就不是一个奇怪的独立语句:

arr := make([]int, 10)
for i := range arr {
    go func(i int) { 
        fmt.Println(i)
    }(i)
}
.对于所有的游乐场版本,我必须添加同步,以便在goroutines运行之前,
main
不会退出

令人困惑的是,每次通过循环“运行”一次的声明的行为不同(范围以奇怪的方式显示),但绕过它的方法非常简单



在您的例子中:
queue这是一个作用域问题。在开始使用
go func(){…}
时,经常会发生这种情况;这可能比并发性的实际问题更常见。本文中有相关章节

您创建的匿名
func
获取对外部作用域中变量的引用,而不是对
func
语句运行时的值的引用。并且循环变量在每次通过循环时都会更新,因此它会在
go
语句和goroutine实际运行时发生变化

您可以通过在循环的每次迭代期间声明另一个变量来解决此问题(即,在
for
大括号内)。如果您的循环是
for i:=range arr{…}
您可以只添加
i:=i
。因此,此错误版本:

arr := make([]int, 10)
for i := range arr {
    go func() { 
        fmt.Println(i)
    }()
}
..固定版本,在循环内重新定义
i

arr := make([]int, 10)
for i := range arr {
    i := i
    go func() { 
        fmt.Println(i)
    }()
}
..重新声明
i
的另一种可能更优雅的方式是将其作为匿名
func
的参数;这样它就不是一个奇怪的独立语句:

arr := make([]int, 10)
for i := range arr {
    go func(i int) { 
        fmt.Println(i)
    }(i)
}
.对于所有的游乐场版本,我必须添加同步,以便在goroutines运行之前,
main
不会退出

令人困惑的是,每次通过循环“运行”一次的声明的行为不同(范围以奇怪的方式显示),但绕过它的方法非常简单


在您的情况下:
队列
为什么会出现死锁?我希望出现一个无限循环(因为我正在重新计算生成更多元素的元素的数量等等)。范围队列不应该总是侦听通道吗

当通道上的范围循环到达通道缓冲区的末尾(如果通道有缓冲区)时,它将阻塞,等待更多的元素通过通道发送。如果没有其他正在运行或可运行的goroutine,则我们已达到死锁

最简单的例子:

两个不同的goroutine是否对同一个元素进行出列

不。发生的是这一行(26):

go func(){queue
为什么会出现死锁?我希望出现一个无限循环(因为我正在重新计算生成更多元素的元素的数量等等)。范围队列不应该总是侦听通道吗

当通道上的范围循环到达通道缓冲区的末尾(如果通道有缓冲区)时,它将阻塞,等待更多的元素通过通道发送。如果没有其他正在运行或可运行的goroutine,则我们已达到死锁

最简单的例子:

两个不同的goroutine是否对同一个元素进行出列

不。发生的是这一行(26):


go-func(){queue if you change
go-func(){queue twotwo的答案用
go-func(){queue u if you change
go-func(){queue twotwo的答案用
go-func()为我清除了它{queueu谢谢。我想我理解了潜在的问题,但我真的不明白为什么
new:=new
可以解决这个问题。我们不是有goroutine运行时的问题吗?为什么每次都要确保有一个新的值?有没有链接可以让我读到更多关于这个的内容?是的,这很混乱。关键是这是每次在循环括号内声明的内容都是一个“不同的变量”。在函数作用域或
range
子句中声明的变量不是。在中有介绍,但我认为这并没有深入到“深层原因”中。(将部分内容编辑到答案中。)添加了范围问题的独立描述,与具体情况分开(部分原因是它可能对搜索者有用),但请告诉我它是否仍然令人困惑,以及