1. select的使用

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

1.1 一些使用规范

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

 package main

 import "fmt"

 func main() {
ch := make (chan int, ) ch<-
select {
case <-ch:
fmt.Println("随机一")
case <-ch:
fmt.Println("随机二n")
}
}

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

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

1.2 select的应用场景

timeout 机制(超时判断)
 package main

 import (
"fmt"
"time"
) func main() {
timeout := make (chan bool, )
go func() {
time.Sleep(*time.Second) // 休眠1s,如果超过1s还没I操作则认为超时,通知select已经超时啦~
timeout <- true
}()
ch := make (chan int)
select {
case <- ch:
case <- timeout:
fmt.Println("超时啦!")
}
}

  也可以这么写:

 package main

 import (
"fmt"
"time"
) func main() {
ch := make (chan int)
select {
case <-ch:
case <-time.After(time.Second * ): // 利用time来实现,After代表多少时间后执行输出东西
fmt.Println("超时啦!")
}
}

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

 package main

 import (
"fmt"
) func main() {
ch := make (chan int, ) // 注意这里给的容量是1
ch <-
select {
case ch <- :
default:
fmt.Println("通道channel已经满啦,塞不下东西了!")
}
}

  退出机制

 package main

 import (
"fmt"
"time"
) func main() {
i :=
ch := make(chan string, )
defer func() {
close(ch)
}() go func() {
DONE:
for {
time.Sleep(*time.Second)
fmt.Println(time.Now().Unix())
i++ select {
case m := <-ch:
println(m)
break DONE // 跳出 select 和 for 循环
default:
}
}
}() time.Sleep(time.Second * )
ch<-"stop"
}

2. select的实现

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

  select {
case v = <-c:
...foo
default:
...bar
}

  会被编译为:

  if selectnbrecv(&v, c) {
...foo
} else {
...bar
}

  类似地

  select {
case v, ok = <-c:
... foo
default:
... bar
}

  会被编译为:

  if c != nil && selectnbrecv2(&v, &ok, c) {
... foo
} else {
... bar
}

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

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

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

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

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

3. select死锁

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

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

 package main
func main() {
ch := make(chan string)
select {
case <-ch:
}
}

  预防的话加default。

  空select,也会引起死锁。

 package main

 func main() {
select {}
}

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语句。
 package main                                                                                                                                               import "time"
import "fmt"
func main() { c1 := make(chan string)
c2 := make(chan string) go func() {
time.Sleep(time.Second * ) c1 <- "one"
}() go func() {
time.Sleep(time.Second * ) c2 <- "two"
}() for i := ; i < ; i++ {
select { case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}

switch

  switch可以为各种类型进行分支操作, 设置可以为接口类型进行分支判断(通过i.(type))。switch 分支是顺序执行的,这和select不同。
 package main
import "fmt"
import "time" func main() {
i :=
fmt.Print("Write ", i, " as ")
switch i {
case :
fmt.Println("one")
case :
fmt.Println("two")
case :
fmt.Println("three")
}
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("It's the weekend")
default:
fmt.Println("It's a weekday")
}
t := time.Now()
switch {
case t.Hour() < :
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
whatAmI := func(i interface{}) {
switch t := i.(type) {
case bool:
fmt.Println("I'm a bool")
case int:
fmt.Println("I'm an int")
default:
fmt.Printf("Don't know type %T\n", t)
}
}
whatAmI(true)
whatAmI()
whatAmI("hey")
}

go中的关键字-select的更多相关文章

  1. 搜索sqlserver 存储过程中的关键字

    搜索sqlserver 存储过程中的关键字 select * from sys.all_sql_modules where definition like '%SP_NAME%'

  2. SQLserver查询作业、视图、函数、存储过程中的关键字

    一.查询视图.函数.存储过程中的关键字 SELECT a.name,a.[type],b.[definition] FROM sys.all_objects a,sys.sql_modules b W ...

  3. PostgreSQL中关于关键字(保留字)在表名和字段名中的应用文件解决

    标识符和关键词 受限标识符或被引号修饰的标识符.它是由双引号(")包围的一个任意字符序列.一个受限标识符总是一个标识符而不会是一个关键字.因此"select"可以用于引用 ...

  4. 【SQL语句】 - 在所有存储过程中查找关键字,关键字不区分大小写 [sp_findproc]

    USE [EShop]GO/****** Object: StoredProcedure [dbo].[sp_findproc] Script Date: 2015/8/19 11:05:24 *** ...

  5. 如何查询oracle中的关键字

    如何查询oracle中的关键字,执行: select * from v$reserved_words

  6. 【C#学习笔记之一】C#中的关键字

    C#中的关键字 关键字是对编译器具有特殊意义的预定义保留标识符.它们不能在程序中用作标识符,除非它们有一个 @ 前缀.例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字. 下面是列出的 ...

  7. linq中let关键字学习

    linq中let关键字就是对子查询的一个别名,let子句用于在查询中添加一个新的局部变量,使其在后面的查询中可见. linq中let关键字实例 1.传统下的子查询与LET关键字的区别     C# 代 ...

  8. C#中var关键字用法分析

    原文连接 本文实例分析了C#中var关键字用法.分享给大家供大家参考.具体方法如下: C#关键字是伴随着.NET 3.5以后,伴随着匿名函数.LINQ而来, 由编译器帮我们推断具体的类型.总体来说,当 ...

  9. golang关键字select的三个例子, time.After模拟socket/心跳超时

    golang关键字select的三个例子, time.After模拟socket/心跳超时   例子1 select会随机选择一个可执行的case   // 这个例子主要说明select是随机选择一个 ...

随机推荐

  1. Spring Security 入门 (二)

    我们在篇(一)中已经谈到了默认的登录页面以及默认的登录账号和密码. 在这一篇中我们将自己定义登录页面及账号密码. 我们先从简单的开始吧:设置自定义的账号和密码(并非从数据库读取),虽然意义不大. 上一 ...

  2. springboot 快速开发的定制补充

    增强 SpringBoot 快速开发工具 项目地址:https://gitee.com/sanri/web-ui 优点:这是一个 web 通用配置的组件,即插即用,可用于新项目或私活.是对 Sprin ...

  3. 全平台正向tcp端口转发工具rinetd的使用

    Linux下做地址NAT有很多种方法.比如haproxy.nginx的4层代理,linux自带的iptables等都能实现.其实,Linux下有一个叫rinetd的工具,安装简单,配置也不复杂. 下载 ...

  4. Oier们的幸运数字

    题目描述 JerryC对数字痴迷到了一种非正常的境界.每天JerryC都有喜欢的一些数字.第 iii 天JerryC就喜欢Ai−BiA_i-B_iAi​−Bi​中的数字.但是他觉得这样并不是很有趣,于 ...

  5. caffe中batch norm源码阅读

    1. batch norm 输入batch norm层的数据为[N, C, H, W], 该层计算得到均值为C个,方差为C个,输出数据为[N, C, H, W]. <1> 形象点说,均值的 ...

  6. 网络IO的虚拟化模型小结

    网络IO的虚拟化模型随着技术发展,出现了多种方式,例如emulation.para-virtualization.pass-through和SR-IOV等,本文试图对其做一个简单的总结. Emulat ...

  7. Linux下yum与apt-get

    linux系统基本上分两大类: 1.RedHat系列:Redhat.Centos.Fedora等 2.Debian系列:Debian.Ubuntu等 RedHat 系列 1 常见的安装包格式 rpm包 ...

  8. 微信小程序文件压缩上传

    试用场景:上传图片过大,需进行压缩处理. 涉及微信API API 说明 文档 chooseImage 选择图片 https://developers.weixin.qq.com/miniprogram ...

  9. SpringBoot学习(二)探究Springboot启动机制

    引言: SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起探究一下SpringBoot的启动原 ...

  10. Java基础(二十四)Java IO(1)输入/输出流

    在Java API中,可以从其中读入一个字节序列的对象称作输入流,而可以向其中写入一个字节序列的对象称为输出流. 输入流的指向称为源,程序从指向源的输入流中读取数据. 输出流的指向是字节要去的目的地, ...