为什么select case将我的for循环切成两半?

lin*_*ngz 3 go

在下面的代码片段中,tasks是一个长度为30的缓冲通道,其中只包含30个元素.我正在写一个for循环来操作每个任务,从一个通道读入.

for i := 0; i < len(tasks); i++ {
    fmt.Println(i)
    select {
      case task := <-tasks: 
        fmt.Println(task)
        // Do something

    }
}

fmt.Println("Done")
Run Code Online (Sandbox Code Playgroud)

但是,这个for循环只从0到14运行.当我改变这个通道的长度(这取决于我有的任务元素的数量)时,for循环总是只运行一半len(tasks).为什么会这样?

背景:我使用缓冲通道执行任务,因为我打算在goroutine中执行每个任务,如果失败则处理任务.但是我现在已经将代码切换到for循环中的一个选择的情况,我很困惑为什么select case导致for循环只执行一半的时间.我确认这个for循环已经完成执行,并且在最后一次执行之后,i等于14(通道长度为30).

roo*_*tar 6

len(task) 如果你读的话会减少 tasks

使用空for来迭代地从任务中读取所有内容.

for {
    select {
    case task := <-tasks:
        fmt.Println(task)
        // Do something
    }
    if len(tasks) == 0 {break}
}
Run Code Online (Sandbox Code Playgroud)

或者您也可以使用范围:

for task := range tasks {

    fmt.Println(task)
    // Do something

    if len(tasks) == 0 {break}
}
Run Code Online (Sandbox Code Playgroud)

break如果您不想锁定goroutine,请不要忘记此循环.


T. *_*rie 6

len(tasks)在每次迭代时重新评估.所以值是30,然后是29,然后是28,......最后,在中间len(tasks)交叉i,这就是它只打印一半值的原因.

那么,你可以做的是将长度存储在一个变量中:

n := len(tasks)
for i := 0; i < n; i++ { ... }
Run Code Online (Sandbox Code Playgroud)

或者,你可以简单地写

for len(tasks) > 0 { ... }
Run Code Online (Sandbox Code Playgroud)

如果要使用所有元素,range运算符的工作原理是:

for task := range tasks { ... }
Run Code Online (Sandbox Code Playgroud)

此外,请记住在使用完通道后关闭通道.

  • @lingz,这就是Go中循环的东西.循环变量被分配1并在每次迭代时被覆盖.闭包接收指向它的指针,而不是副本.因此,你的goroutine将在最后一次迭代中运行相同的值.有两个解决方案 - https://play.golang.org/p/utTHzgcx4C https://play.golang.org/p/pJnzf_zPYX (2认同)