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

时间:2016-06-04 20:26:25

标签: 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")

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

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

3 个答案:

答案 0 :(得分:6)

如果您从len(task)

中读取,则

tasks会减少

使用空for迭代读取任务中的所有内容。

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

或者您也可以使用范围:

for task := range tasks {

    fmt.Println(task)
    // Do something

    if len(tasks) == 0 {break}
}

如果您不想锁定goroutine,请不要忘记break这个周期。

答案 1 :(得分:6)

每次迭代都会重新评估

len(tasks)。所以值是30,然后是29,然后是28,......最后,len(tasks)在中间穿过i,这就是为什么它只打印了一半的值。

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

n := len(tasks)
for i := 0; i < n; i++ { ... }

或者,你可以简单地写

for len(tasks) > 0 { ... }

如果要使用所有元素,range运算符将起作用:

for task := range tasks { ... }

另外,请务必在使用完毕后关闭频道。

答案 2 :(得分:0)

因为你从频道中取元素,它的长度减少了.. 看看这个样本,我想你会很清楚:
https://play.golang.org/p/TC3HymIlhs