Go 的通道有两种操作方式,一种是带 range 子句的 for 语句,另一种则是 select 语句,它是专门为了操作通道而存在的。这里主要介绍 select 的用法。

一、select的语法

select 语句的语法如下:

select {
case <-ch1 :
statement(s)
case ch2 <- 1 :
statement(s)

default : /* 可选 */
statement(s)
}

这里要注意:

  • 每个 case 都必须是一个通信。

    由于 select 语句是专为通道设计的,所以每个 case 表达式中都只能包含操作通道的表达式,比如接收表达式。
  • 如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行。
  • 如果多个 case 都不能运行,若有 default 子句,则执行该语句,反之,select 将阻塞,直到某个 case 可以运行。
  • 所有 channel 表达式都会被求值。

用一个简单示例看一下:

package main

import (
"fmt"
"math/rand"
) func main() {
// 准备好几个通道。
intChannels := [5]chan int{
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
make(chan int, 1)
}
// 随机选择一个通道,并向它发送元素值。
index := rand.Intn(5)
fmt.Printf("The index: %d\n", index)
intChannels[index] <- index
// 哪一个通道中有可取的元素值,哪个对应的分支就会被执行。
select {
case <-intChannels[0]:
fmt.Println("The first candidate case is selected.")
case <-intChannels[1]:
fmt.Println("The second candidate case is selected.")
case elem := <-intChannels[2]:
fmt.Printf("The third candidate case is selected. The element is %d.\n", elem)
default:
fmt.Println("No candidate case is selected!")
}
}

准备了5个通道,放到一个数组里,并用0-4的随机数作为数组的索引,向通道发送元素。用一个包含了三个候选分支的 select 语句,分别尝试从三个通道中接收元素值,哪一个通道中有值,哪一个对应的候选分支就会被执行。

执行结果如下:

The index: 1
The second candidate case is selected.

多次执行的话,会随机输出不同的字符串,如果随机值不是0、1、2,则会执行 default 语句。

二、select死锁

select 使用不当会发生死锁。

  • 如果通道没有数据发送,但 select 中有存在接收通道数据的语句,将发生死锁。
package main
func main() {
ch := make(chan string)
select {
case <-ch:
}
}

报错如下:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
/workspace/src/test.go:5 +0x52
exit status 2

可以添加 default 语句来避免产生死锁。

  • 空 select{}
package main
func main() {
select {}
}

报错如下:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:
main.main()
/workspace/src/test.go:3 +0x20
exit status 2
三、select和for结合使用

select 语句只能对其中的每一个case表达式各求值一次。所以,如果想连续或定时地操作其中的通道的话,就需要通过在for语句中嵌入select语句的方式实现。

package main

import (
"fmt"
"time"
) func main() {
tick := time.Tick(time.Second)
for {
select {
case t := <-tick:
fmt.Println(t)
break
}
}
fmt.Println("end")
}

打印结果如下:

2021-10-10 23:40:59.664804 +0800 CST m=+1.001254136
2021-10-10 23:41:00.665263 +0800 CST m=+2.001696651
2021-10-10 23:41:01.665595 +0800 CST m=+3.002013571
2021-10-10 23:41:02.665293 +0800 CST m=+4.001699053
2021-10-10 23:41:03.665308 +0800 CST m=+5.001702570
2021-10-10 23:41:04.666859 +0800 CST m=+6.003244115
2021-10-10 23:41:05.665595 +0800 CST m=+7.001972958
……

你会发现 break 只跳出了 select,无法跳出for。

解决办法有两种:

  • 使用 goto 跳出循环
package main

import (
"fmt"
"time"
) func main() {
tick := time.Tick(time.Second)
for {
select {
case t := <-tick:
fmt.Println(t)
//跳到指定位置
goto END
}
}
END:
fmt.Println("end")
}
  • 使用标签
package main

import (
"fmt"
"time"
) func main() {
tick := time.Tick(time.Second)
//这是标签
FOREND:
for {
select {
case t := <-tick:
fmt.Println(t)
//跳出FOREND标签
break ForEnd
}
}
END:
fmt.Println("end")
}

Go的Select的更多相关文章

  1. 最全的ORACLE-SQL笔记

    -- 首先,以超级管理员的身份登录oracle sqlplus sys/bjsxt as sysdba --然后,解除对scott用户的锁 alter user scott account unloc ...

  2. Matplotlib数据可视化(6):饼图与箱线图

    In [1]: from matplotlib import pyplot as plt import numpy as np import matplotlib as mpl mpl.rcParam ...

  3. SELECT INTO 和 INSERT INTO SELECT 两种表复制语句

    Insert是T-sql中常用语句,Insert INTO table(field1,field2,...) values(value1,value2,...)这种形式的在应用程序开发中必不可少.但我 ...

  4. select、poll、epoll之间的区别总结

    select.poll.epoll之间的区别总结 05/05. 2014 select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪 ...

  5. LINQ to SQL Select查询

    1. 查询所有字段 using (NorthwindEntities context = new NorthwindEntities()) { var order = from n in contex ...

  6. ADO.NET一小记-select top 参数问题

    异常处理汇总-后端系列 http://www.cnblogs.com/dunitian/p/4523006.html 最近使用ADO.NET的时候,发现select top @count xxxx 不 ...

  7. iosselect:一个js picker项目,在H5中实现IOS的select下拉框效果

    具体文档和demo可以访问github:https://github.com/zhoushengmufc/iosselect 移动端浏览器对于select的展示样式是不一致的,ios下是类似原生的pi ...

  8. SQL Server中SELECT会真的阻塞SELECT吗?

    在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...

  9. (转载) Linux IO模式及 select、poll、epoll详解

    注:本文是对众多博客的学习和总结,可能存在理解错误.请带着怀疑的眼光,同时如果有错误希望能指出. 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案 ...

  10. 基于select的python聊天室程序

    python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...

随机推荐

  1. SQL查询数据库名、表名、列名

    1.获取所有用户名SELECT name FROM Sysusers where status='2' and islogin='1'islogin='1'表示帐户islogin='0'表示角色sta ...

  2. Ubuntu16.04 Linux 下无痛安装、配置Gogs

    本文在Win7+VMware的ubuntu 16.04中测试,安装Gogs,Install from binary. 准备工作: sudo apt-get install git sudo addus ...

  3. MySQL-SQL基础

    mysql> use test; Database changed mysql> create table emp(ename varchar(10),hirdate date,sal d ...

  4. 一、部署sqlserver

    1.下载并挂载sqlserver镜像 2.填写秘钥:6GPYM-VHN83-PHDM2-Q9T2R-KBV83 3.默认下一步 4.勾选需要的功能 5.默认下一步 6.默认下一步 等待安装完成即可. ...

  5. multipass指定virualbox搭建k8s集群(选择docker作为默认容器)

    目录 前言 步骤 初始化三台虚拟机 统一安装docker 修改docker镜像源 查看masterIP 安装master节点(重点设置) 查看master的token 安装worker节点 测试 部署 ...

  6. TCP协议和套接字

    一.TCP通信概述,逻辑连接就是三次握手 二.客户端和服务端实现TCP协议通信基本步骤 1.客户端套接字对象 Socket 2.服务端套接字ServerSocket 客户端补充完整代码:除了创建各自的 ...

  7. python工作中总结

    以下方法平时很少用,用其它解决方法,只是总结了一些其它同事的技巧 1   如何自动生成列表,加一个条件还能筛选 [x for x in range(10) if x>5 ] 来生一个字典试试 d ...

  8. UVA 11853 Paintball(几何数学+DFS)

    https://vjudge.net/problem/UVA-11853 根据题意描述,相当于在一个正方形中有若干个圆形障碍物,问是否能从左边界走到右边界.判断是否有解需要一点创造性的思维:不妨把正方 ...

  9. 自己实现Controller——标准型

    标准Controller 上一篇通过一个简单的例子,编写了一个controller-manager,以及一个极简单的controller.从而对controller的开发有个最基本的认识,但是细心观察 ...

  10. linux停止进程

    $ kill -QUIT 主进程号 例如:kill -QUIT 1708 这种方式相对来说会有一个停止的过程,先将子进程停止掉,然后再停掉主进程. 快速停止Nginx: $ kill -TERM 主进 ...