zoukankan      html  css  js  c++  java
  • go中的关键字-select

    1. select的使用

      定义:在golang里头select的功能与epoll(nginx)/poll/select的功能类似,都是坚挺IO操作,当IO操作发生的时候,触发相应的动作。

    1.1 一些使用规范

      在Go的语言规范中,select中的case的执行顺序是随机的,当有多个case都可以运行,select会随机公平地选出一个执行,其他的便不会执行:

     1 package main
     2 
     3 import "fmt"
     4 
     5 func main() {
     6     ch := make (chan int, 1)
     7 
     8     ch<-1
     9     select {
    10     case <-ch:
    11         fmt.Println("随机一")
    12     case <-ch:
    13         fmt.Println("随机二n")
    14     }
    15 }

      输出内容为随机一二里面的任意一个。

      case后面必须是channel操作,否则报错;default子句总是可运行的,所以没有default的select才会阻塞等待事件 ;没有运行的case,那么将会阻塞事件发生报错(死锁)。

    1.2 select的应用场景

    timeout 机制(超时判断)
     1 package main
     2 
     3 import (
     4     "fmt"
     5     "time"
     6 )
     7 
     8 func main() {
     9     timeout := make (chan bool, 1)
    10     go func() {
    11         time.Sleep(1*time.Second) // 休眠1s,如果超过1s还没I操作则认为超时,通知select已经超时啦~
    12         timeout <- true
    13     }()
    14     ch := make (chan int)
    15     select {
    16     case <- ch:
    17     case <- timeout:
    18         fmt.Println("超时啦!")
    19     }
    20 }

      也可以这么写:

     1 package main
     2 
     3 import (
     4     "fmt"
     5     "time"
     6 )
     7 
     8 func main() {
     9     ch := make (chan int)
    10     select {
    11     case <-ch:
    12     case <-time.After(time.Second * 1): // 利用time来实现,After代表多少时间后执行输出东西
    13         fmt.Println("超时啦!")
    14     }
    15 }

      判断channel是否阻塞(或者说channel是否已经满了)

     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func main() {
     8     ch := make (chan int, 1)  // 注意这里给的容量是1
     9     ch <- 1
    10     select {
    11     case ch <- 2:
    12     default:
    13         fmt.Println("通道channel已经满啦,塞不下东西了!")
    14     }
    15 }

      退出机制

     1 package main
     2 
     3 import (
     4     "fmt"
     5     "time"
     6 )
     7 
     8 func main() {
     9     i := 0
    10     ch := make(chan string, 0)
    11     defer func() {
    12         close(ch)
    13     }()
    14 
    15     go func() {
    16         DONE: 
    17         for {
    18             time.Sleep(1*time.Second)
    19             fmt.Println(time.Now().Unix())
    20             i++
    21 
    22             select {
    23             case m := <-ch:
    24                 println(m)
    25                 break DONE // 跳出 select 和 for 循环
    26             default:
    27             }
    28         }
    29     }()
    30 
    31     time.Sleep(time.Second * 4)
    32     ch<-"stop"
    33 }

    2. select的实现

      select-case中的chan操作编译成了if-else。如:

    1  select {
    2  case v = <-c:
    3          ...foo
    4  default:
    5          ...bar
    6  }

      会被编译为:

    1  if selectnbrecv(&v, c) {
    2          ...foo
    3  } else {
    4          ...bar
    5  }

      类似地

    1  select {
    2  case v, ok = <-c:
    3      ... foo
    4  default:
    5      ... bar
    6  }

      会被编译为:

    1  if c != nil && selectnbrecv2(&v, &ok, c) {
    2      ... foo
    3  } else {
    4      ... bar
    5  }

      selectnbrecv函数只是简单地调用runtime.chanrecv函数,不过是设置了一个参数,告诉当runtime.chanrecv函数,当不能完成操作时不要阻塞,而是返回失败。也就是说,所有的select操作其实都仅仅是被换成了if-else判断,底层调用的不阻塞的通道操作函数。

      在Go的语言规范中,select中的case的执行顺序是随机的,那么,如何实现随机呢?

      select和case关键字使用了下面的结构体:

    1 struct    Scase
    2   {
    3       SudoG    sg;            // must be first member (cast to Scase)
    4       Hchan*    chan;        // chan
    5       byte*    pc;            // return pc
    6       uint16    kind;
    7       uint16    so;            // vararg of selected bool
    8       bool*    receivedp;    // pointer to received bool (recv2)
    9   };
    1  struct    Select
    2      {
    3      uint16    tcase;            // 总的scase[]数量
    4      uint16    ncase;            // 当前填充了的scase[]数量
    5      uint16*    pollorder;        // case的poll次序
    6      Hchan**    lockorder;        // channel的锁住的次序
    7      Scase    scase[1];        // 每个case会在结构体里有一个Scase,顺序是按出现的次序
    8  };

      每个select都对应一个Select结构体。在Select数据结构中有个Scase数组,记录下了每一个case,而Scase中包含了Hchan。然后pollorder数组将元素随机排列,这样就可以将Scase乱序了。

     3. select死锁

      select不注意也会发生死锁,分两种情况:

      如果没有数据需要发送,select中又存在接收通道数据的语句,那么将发送死锁

    1 package main
    2 func main() {  
    3     ch := make(chan string)
    4     select {
    5     case <-ch:
    6     }
    7 }

      预防的话加default。

      空select,也会引起死锁。

    1 package main
    2 
    3 func main() {  
    4     select {}
    5 }

     4. select和switch的区别

    select

    select只能应用于channel的操作,既可以用于channel的数据接收,也可以用于channel的数据发送。如果select的多个分支都满足条件,则会随机的选取其中一个满足条件的分支, 如规范中所述:
    If multiple cases can proceed, a uniform pseudo-random choice is made to decide which single communication will execute.
    `case`语句的表达式可以为一个变量或者两个变量赋值。有default语句。
    31 package main                                                                                                                                              32 import "time"
    33 import "fmt"                                                                                                                                              
    35 func main() {                                                                                                                                             36     c1 := make(chan string)
    37     c2 := make(chan string)                                                                                                                               38     go func() {
    39         time.Sleep(time.Second * 1)                                                                                                                       40         c1 <- "one"
    41     }()                                                                                                                                                   42     go func() {
    43         time.Sleep(time.Second * 2)                                                                                                                       44         c2 <- "two"
    45     }()                                                                                                                                                   46     for i := 0; i < 2; i++ {
    47         select {                                                                                                                                          48             case msg1 := <-c1:
    49             fmt.Println("received", msg1)          
    50 case msg2 := <-c2: 51 fmt.Println("received", msg2)
    52 } 53 }

    switch

      switch可以为各种类型进行分支操作, 设置可以为接口类型进行分支判断(通过i.(type))。switch 分支是顺序执行的,这和select不同。
     1 package main                  
     2 import "fmt"
     3 import "time"  
     4 
     5 func main() {                                                                                                                             
     6      i := 2
     7      fmt.Print("Write ", i, " as ")  
     8      switch i {
     9          case 1:
    10          fmt.Println("one")
    11          case 2:                                                                                                                                  
    12          fmt.Println("two")
    13          case 3:                                                                                                                      
    14          fmt.Println("three")
    15      }                                                                                                                                             
    16      switch time.Now().Weekday() {
    17          case time.Saturday, time.Sunday:
    18          fmt.Println("It's the weekend")
    19          default:                                                                                                                                      
    20          fmt.Println("It's a weekday")
    21      }                                                                                                                                                 
    22      t := time.Now()
    23      switch {                                                                                                                                         
    24          case t.Hour() < 12:
    25          fmt.Println("It's before noon")                                                                                                              
    26          default:
    27          fmt.Println("It's after noon")                                                                                                                  
    28      }
    29      whatAmI := func(i interface{}) {                                                                                                                   
    30          switch t := i.(type) {
    31              case bool:                                                                                                                              
    32              fmt.Println("I'm a bool")
    33              case int:                                                                                                                                 
    34              fmt.Println("I'm an int")
    35              default:                                                                                                                                 
    36              fmt.Printf("Don't know type %T
    ", t)
    37          }
    38      }
    39      whatAmI(true)                                                                                                                                     
    40      whatAmI(1)
    41      whatAmI("hey")                                                                                                                                 
    42  }
  • 相关阅读:
    《Thinking In C#》
    在图片上写字
    在设计期跟踪代码
    VS2003下的重构工具ReSharp
    监视剪贴板的变化
    一次重构导向设计模式的实践
    JENA学习的零散笔记
    jena处理Owl
    Maven库中.lastUpdated文件自动清除工具
    WEB数据挖掘(六)——Aperture数据抽取(2)
  • 原文地址:https://www.cnblogs.com/33debug/p/11891154.html
Copyright © 2011-2022 走看看