Go select使用与底层原理讲解

目录
  • 1. select的使用
  • 2. 底层原理
  • 3. 数据结构
  • 4. 几种常见 case
    • case 1
    • case2
    • case3
    • case4

1. select的使用

select 是 Go 提供的 IO 多路复用机制,可以用多个 case 同时监听多个 channl 的读写状态:

  • case: 可以监听 channl 的读写信号
  • default:声明默认操作,有该字段的 select 不会阻塞
select {
case chan <-:
    // TODO
case <- chan:
    // TODO
default:
    // TODO
}

2. 底层原理

  • 每一个 case 对应的 channl 都会被封装到一个结构体中;
  • 当第一次执行到 select 时,会锁住所有的 channl 并且,打乱 case 结构体的顺序;
  • 按照打乱的顺序遍历,如果有就绪的信号,就直接走对应 case 的代码段,之后跳出 select;
  • 如果没有就绪的代码段,但是有 default 字段,那就走 default 的代码段,之后跳出 select;
  • 如果没有 default,那就将当前 goroutine 加入所有 channl 的对应等待队列;
  • 当某一个等待队列就绪时,再次锁住所有的 channl,遍历一遍,将所有等待队列中的 goroutine 取出,之后执行就绪的代码段,跳出select。

3. 数据结构

每一个 case 对应的数据结构如下:

type scase struct {
    c           *hchan         // chan
    elem        unsafe.Pointer // 读或者写的缓冲区地址
    kind        uint16   //case语句的类型,是default、传值写数据(channel <-) 还是  取值读数据(<- channel)
    pc          uintptr // race pc (for race detector / msan)
    releasetime int64
}

4. 几种常见 case

学习了 select 的使用与原理,我们就能更轻松地分辨不同情况下的输出情况了。

case 1

package main

import (
  "fmt"
  "time"
)

func main() {
  chan1 := make(chan int)
  chan2 := make(chan int)
  go func() {
    chan1 <- 1
    time.Sleep(5 * time.Second)
  }()
  go func() {
    chan2 <- 1
    time.Sleep(5 * time.Second)
  }()
  select {
    case <- chan1:
      fmt.Println("chan1")
    case <- chan2:
      fmt.Println("chan2")
    default:
      fmt.Println("default")
  }
}

三种输出都有可能。

case2

package main

import (
  "fmt"
  "time"
)
func main() {
  chan1 := make(chan int)
  chan2 := make(chan int)

  select {
    case <- chan1:
      fmt.Println("chan1")
    case <- chan2:
      fmt.Println("chan2")
  }
  fmt.Println("main exit.")
}

上述程序会一直阻塞。

case3

package main

import (
  "fmt"
)

func main() {
  chan1 := make(chan int)
  chan2 := make(chan int)

  go func() {
    close(chan1)
  }()
  go func() {
    close(chan2)
  }()
  select {
    case <- chan1:
      fmt.Println("chan1")
    case <- chan2:
      fmt.Println("chan2")
  }
  fmt.Println("main exit.")
}

随机执行1或者2.

case4

package main

func main() {
  select {
  }
}

对于空的 select 语句,程序会被阻塞,确切的说是当前协程被阻塞,同时 Go 自带死锁检测机制,当发现当前协程再也没有机会被唤醒时,则会发生 panic。所以上述程序会 panic。

到此这篇关于Go select使用与底层原理讲解的文章就介绍到这了,更多相关Go select使用 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入了解Go的interface{}底层原理实现

    目录 1. interface{}初探 2. eface 3. iface 4. 接口转化 1. interface{}初探 Go是强类型语言,各个实例变量的类型信息正是存放在interface{}中的,Go中的反射也与其底层结构有关. iface 和 eface 都是 Go 中描述interface{}的底层结构体,区别在于 iface 描述的接口包含方法,而 eface 则是不包含任何方法的空接口:interface{}. 接下来,我们将详细剖析iface 和 eface的底层数据结构. 2

  • matplotlib之多边形选区(PolygonSelector)的使用

    多边形选区概述 多边形选区是一种常见的对象选择方式,在一个子图中,单击鼠标左键即构建一个多边形的端点,最后一个端点与第一个端点重合即完成多边形选区,选区即为多个端点构成的多边形.在matplotlib中的多边形选区属于部件(widgets),matplotlib中的部件都是中性(neutral )的,即与具体后端实现无关. 多边形选区具体实现定义为matplotlib.widgets.PolygonSelector类,继承关系为:Widget->AxesWidget->_SelectorWid

  • Golang的select多路复用及channel使用操作

    看到有个例子实现了一个类似于核弹发射装置,在发射之前还是需要随时能输入终止发射. 这里就可以用到cahnnel 配合select 实现多路复用. select的写法用法有点像switch.但是和switch不同的是,select的一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成的一个语句块.现在让我们来实现一下这个核弹发射器 package main import ( "fmt" "time" "os"

  • Go语言中的并发goroutine底层原理

    目录 一.基本概念 ①并发.并行区分 ②从用户态线程,内核态线程阐述go与java并发的优劣 ②高并发为什么是Go语言强项? ③Go语言实现高并发底层GMP模型原理解析 二.上代码学会Go语言并发 ①.开启一个简单的线程 ②.动态的关闭线程 一.基本概念 ①并发.并行区分 1.概念 并发:同一时间段内一个对象执行多个任务,充分利用时间 并行:同一时刻,多个对象执行多个任务 2.图解 类似于超市柜台结账,并行是多个柜台结多个队列,在计算机中是多核cpu处理多个go语言开启的线程,并发是一个柜台结账

  • Go语言CSP并发模型goroutine channel底层实现原理

    目录 Go的CSP并发模型(goroutine + channel) 1.goroutine goroutine的优点: 2.channel 无缓存channel 有缓存channel 3.Go并发模型的底层实现原理 4.一个CSP例子 参考Go的CSP并发模型实现:M, P, G Go语言是为并发而生的语言,Go语言是为数不多的在语言层面实现并发的语言. 并发(concurrency):多个任务在同一段时间内运行. 并行(parallellism):多个任务在同一时刻运行. Go的CSP并发模

  • Go语言使用select{}阻塞main函数介绍

    很多时候我们需要让main函数不退出,让它在后台一直执行,例如: func main() { for i := 0; i < 20; i++ { //启动20个协程处理消息队列中的消息 c := consumer.New() go c.Start() } select {} // 阻塞 } 可能大多数人想到阻塞的方法是用channel,当然都是可以的,不过用select{}更加简洁 :) 补充:由浅入深聊聊Golang中select的实现机制 正文 话说今天在玩select的时候发现一个问题,是

  • Golang 语言map底层实现原理解析

    在开发过程中,map是必不可少的数据结构,在Golang中,使用map或多或少会遇到与其他语言不一样的体验,比如访问不存在的元素会返回其类型的空值.map的大小究竟是多少,为什么会报"cannot take the address of"错误,遍历map的随机性等等. 本文希望通过研究map的底层实现,以解答这些疑惑. 基于Golang 1.8.3 1. 数据结构及内存管理 hashmap的定义位于 src/runtime/hashmap.go 中,首先我们看下hashmap和buck

  • go select编译期的优化处理逻辑使用场景分析

    前言 select作为Go chan通信的重要监听工具,有着很广泛的使用场景.select的使用主要是搭配通信case使用,表面上看,只是简单的select及case搭配,实际上根据case的数量及类型,在编译时select会进行优化处理,根据不同的情况调用不同的底层逻辑. select的编译处理 select编译时的核心处理逻辑如下: func walkselectcases(cases *Nodes) []*Node { ncas := cases.Len() sellineno := li

  • go语言中如何使用select的实现示例

    目录 1.基本语法 2.select语句的实际应用 在golang语言中,select语句 就是用来监听和channel有关的IO操作,当IO操作发生时,触发相应的case动作. 有了 select语句,可以实现 main主线程 与 goroutine线程 之间的互动. 1.基本语法 select { case <-ch1 : // 检测有没有数据可读 // 一旦成功读取到数据,则进行该case处理语句 case ch2 <- 1 : // 检测有没有数据可写 // 一旦成功向ch2写入数据,

  • Go select使用与底层原理讲解

    目录 1. select的使用 2. 底层原理 3. 数据结构 4. 几种常见 case case 1 case2 case3 case4 1. select的使用 select 是 Go 提供的 IO 多路复用机制,可以用多个 case 同时监听多个 channl 的读写状态: case: 可以监听 channl 的读写信号 default:声明默认操作,有该字段的 select 不会阻塞 select { case chan <-: // TODO case <- chan: // TOD

  • Python字典的核心底层原理讲解

    字典对象的核心是散列表.散列表是一个稀疏数组(总是有空白元素的数组),数组的每个单元叫做 bucket.每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引用.所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket.下面通过存储与获取数据的过程介绍字典的底层原理. 存储数据的过程 例如,我们将'name' = '张三' 这个键值对存储到字典map中,假设数组长度为8,可以用3位二进制表示. >>> map = {} >>> map

  • SpringBoot超详细深入讲解底层原理

    目录 手写springboot Springboot项目 自动配置 小结 手写springboot 在日常开发中只需要引入下面的依赖就可以开发Servlet进行访问了. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 那这是怎么做到的呢?今天就来

  • 一文彻底搞懂IO底层原理

    目录 一.混乱的 IO 概念 二.用户空间和内核空间 三.IO模型 3.1.BIO(Blocking IO) 3.2."C10K"问题 3.3.NIO非阻塞模型 3.4.IO多路复用模型 3.4.1.select() 3.4.2.poll() 3.4.3.epoll() 四.同步.异步 五.总结 一.混乱的 IO 概念 IO是Input和Output的缩写,即输入和输出.广义上的围绕计算机的输入输出有很多:鼠标.键盘.扫描仪等等.而我们今天要探讨的是在计算机里面,主要是作用在内存.网卡

  • 总结网络IO模型与select模型的Python实例讲解

    网络I/O模型 人多了,就会有问题.web刚出现的时候,光顾的人很少.近年来网络应用规模逐渐扩大,应用的架构也需要随之改变.C10k的问题,让工程师们需要思考服务的性能与应用的并发能力. 网络应用需要处理的无非就是两大类问题,网络I/O,数据计算.相对于后者,网络I/O的延迟,给应用带来的性能瓶颈大于后者.网络I/O的模型大致有如下几种: 同步模型(synchronous I/O) 阻塞I/O(bloking I/O) 非阻塞I/O(non-blocking I/O) 多路复用I/O(multi

  • CodeIgniter连贯操作的底层原理分析

    本文分析了CodeIgniter连贯操作的底层原理.分享给大家供大家参考,具体如下: php oop连贯操作原理 ->符号其实是传递对象指针的.或许这么说是不对的. 但是,我们可以这么的理解. 不多说.放代码. 普通用法: <?php class test { public $a=''; public $b=''; public function actiona() { $this->a="hello"; return $this; } public function

  • Activiti工作流学习笔记之自动生成28张数据库表的底层原理解析

    网上关于工作流引擎Activiti生成表的机制大多仅限于四种策略模式,但其底层是如何实现的,相关文章还是比较少,因此,觉得撸一撸其生成表机制的底层原理. 我接触工作流引擎Activiti已有两年之久,但一直都只限于熟悉其各类API的使用,对底层的实现,则存在较大的盲区. Activiti这个开源框架在设计上,其实存在不少值得学习和思考的地方,例如,框架用到以命令模式.责任链模式.模板模式等优秀的设计模式来进行框架的设计. 故而,是值得好好研究下Activiti这个框架的底层实现. 我在工作当中现

  • 浅谈mysql join底层原理

    目录 join算法 驱动表和非驱动表的区别 1.Simple Nested-Loop Join,简单嵌套-无索引的情况 2.Index Nested-Loop Join-有索引的情况 3.Block Nested-Loop Join ,join buffer缓冲区 缓冲区大小 数据量大的表和数据量小的表如何选择连接顺序 细节 join算法 mysql只支持一种join算法:Nested-Loop Join(嵌套循环连接),但Nested-Loop Join有三种变种: Simple Nested

  • mysql表分区的使用与底层原理详解

    目录 什么是分区表 分区表应用场景 分区表的限制 分区类型 分区表的使用 1.范围分区 2.列表分区(list分区) 3.列分区 4.hash分区 5.秘钥分区(key分区) 6.子分区 添加分区 分区表原理 如何使用分区表 注意事项 总结 什么是分区表 MySQL从5.1版本开始支持分区功能,分区是将一个表的数据按照某种方式,比如按照时间上的月份,分成多个较小的,更容易管理的部分,但是逻辑上仍是一个表. 还没出现分区表的时候,所有的数据都是存放在一个文件里面的,如果数据量太大,查询数据时总是避

  • Go语言上下文context底层原理

    目录 1. context 介绍 2. 基本介绍 3. 源码分析 3.1 Context 接口 3.2 emptyCtx 3.3 cancelCtx 3.4 timerCtx 3.5 valueCtx 4. 使用建议 1. context 介绍 很多时候,我们会遇到这样的情况,上层与下层的goroutine需要同时取消,这样就涉及到了goroutine间的通信.在Go中,推荐我们以通信的方式共享内存,而不是以共享内存的方式通信.所以,就需要用到channl,但是,在上述场景中,如果需要自己去处理

随机推荐