【问题标题】:select with single case blocks, adding default: unblocks选择单个案例块,添加默认值:unblocks
【发布时间】:2016-08-23 17:05:06
【问题描述】:

在使用类似这样的代码测试一些代码时:

// ch := make(chan error)

for {
    select {
      case <- ch:
           println("here")
    }
}

我注意到如果我不添加 default 代码块:

for {
    select {
       case <- ch:
            println("here")
       default:
    }
}

如果需要阻止,最好使用range,例如:

for {
    for _ = range <- ch {
        println("here")
    }
}

或者在这种情况下使用selectrange 有什么区别/优势?

【问题讨论】:

    标签: go


    【解决方案1】:

    1-当你在处理一个频道时,可以使用for
    考虑这个工作代码(The Go Playground):

    package main
    
    import "fmt"
    
    func main() {
        ch := make(chan int, 2)
        ch <- 1
        ch <- 2
        close(ch)
        for range ch {
        }
        fmt.Println("Done.")
    }
    

    这将清空频道。
    注意:您应该close 频道或者您应该使用break 语句来完成该循环。


    2- 当您处理更多频道时,您可以使用select,像这样(The Go Playground):

    for {
        select {
        case <-pause:
            fmt.Println("pause")
            select {
            case <-play:
                fmt.Println("play")
            case <-quit:
                wg.Done()
                return
            }
        case <-quit:
            wg.Done()
            return
        default:
            work()
        }
    }
    

    3- 使用 nil 和封闭通道(The Go Playground):

    package main
    
    import "fmt"
    
    func main() {
        var quit chan struct{} // nil
    
        select {
        case <-quit:
            fmt.Println("1")
        default:
            fmt.Println("2") // This runs
        }
    
        quit = make(chan struct{}, 1)
    
        select {
        case <-quit:
            fmt.Println("10")
        default:
            fmt.Println("20") // This runs
        }
    
        quit <- struct{}{} // send
    
        select {
        case <-quit:
            fmt.Println("100") // This runs
        default:
            fmt.Println("200")
        }
    
        close(quit)
    
        select {
        case <-quit:
            fmt.Println("1000") // This runs
        default:
            fmt.Println("2000")
        }
    
        select {
        case <-quit:
            fmt.Println("10000") // This runs
        default:
            fmt.Println("20000")
        }
    }
    

    输出:

    2
    20
    100
    1000
    10000
    

    Select statements

    “选择”语句选择一组可能的发送或 接收操作将继续。它看起来类似于“开关” 声明,但所有案例都涉及通信 操作。

    带有 RecvStmt 的案例可以将 RecvExpr 的结果分配给一个或 两个变量,可以使用短变量声明 宣言。 RecvExpr 必须是(可能带括号的)接收 手术。最多可以有一个默认情况,它可能会出现 案例列表中的任何位置。

    “选择”语句的执行分几个步骤进行:

    对于语句中的所有情况,receive 的通道操作数 操作以及 send 的通道和右侧表达式 语句在输入时按源顺序只计算一次 “选择”语句。结果是一组要接收的通道 from 或 send to,以及要发送的相应值。任何一方 无论哪种情况(如果有),该评估中的影响都会发生 选择通讯操作继续。上的表达 带有短变量声明的 RecvStmt 的左侧或 任务尚未评估。如果一项或多项通讯 可以继续,可以通过制服选择一个可以继续的人 伪随机选择。否则,如果存在默认情况,则 案例被选中。如果没有默认情况,“select”语句 阻塞,直到至少有一个通信可以进行。除非 所选案例为默认案例,各自通信 执行操作。如果所选用例是带有短的 RecvStmt 变量声明或赋值,左侧表达式 被评估并分配接收到的值(或值)。这 执行所选案例的语句列表。自从沟通 在 nil 通道上永远无法继续,只有 nil 通道的选择和 永远不会阻塞默认情况。

    【讨论】:

      【解决方案2】:

      在您的情况下,似乎一个简单的循环就足够了:

      for _ = range ch {
          fmt.Println("drain")
      }
      

      选择是必需的,如果你有 多个 goroutines 来处理。旅游有range and close的例子。

      select 的另一种情况是幂等通道关闭:https://play.golang.org/p/_Ol42BvuuS

      【讨论】:

      【解决方案3】:

      如果选择中只有一个案例,那么最好使用use for range instead of for { select {} } (gosimple),这是一个S1000 golint 检查。

      for range 代码如下:

      for range ch {
          fmt.Println("here")
      }
      
      

      【讨论】:

      • 如果我想检查频道是否关闭了怎么办?例如,是否可以在 for-range 语句中执行 value, ok := &lt;-ch
      • 其实我发现这真的很有帮助,似乎关闭会打破循环:gobyexample.com/range-over-channels
      猜你喜欢
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 2018-07-28
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      • 2015-03-09
      • 1970-01-01
      相关资源
      最近更新 更多