使用 select 切换协程

简介: 在 Go 中,可以通过关键字select来完成从不同的并发执行的协程中获取值,它和switch控制语句非常相似,也被称作通信开关;它的行为像是“你准备好了吗”的轮询机制;select监听进入通道的数据,也可以是用通道发送值的时候。

在 Go 中,可以通过关键字select来完成从不同的并发执行的协程中获取值,它和switch控制语句非常相似,也被称作通信开关;它的行为像是“你准备好了吗”的轮询机制;select监听进入通道的数据,也可以是用通道发送值的时候。


语法格式:

select {
case u:= <- ch1:
        ...
case v:= <- ch2:
        ...
        ...
default: // no value ready to be received
        ...
}


default 语句是可选的;fallthrough 行为,和普通的 switch 相似,是不允许的。在任何一个 case 中执行 break 或者 return,select 就结束了。


select 做的就是:选择处理列出的多个通信情况中的一个。


  • 如果都阻塞了,会等待直到其中一个可以处理
  • 如果多个可以处理,随机选择一个
  • 如果没有通道操作可以处理并且写了default 语句,它就会执行:default 永远是可运行的(这就是准备好了,可以执行)。


select 中使用发送操作并且有 default 可以确保发送不被阻塞!如果没有 default,select 就会一直阻塞。


select 语句实现了一种监听模式,通常用在(无限)循环中;在某种情况下,通过 break 语句使循环退出。

程序示例

package main
import (
    "fmt"
    "time"
)
func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go pump1(ch1)
    go pump2(ch2)
    go suck(ch1, ch2)
    time.Sleep(1e9)
}
func pump1(ch chan int) {
    for i := 0; ; i++ {
        ch <- i * 2
    }
}
func pump2(ch chan int) {
    for i := 0; ; i++ {
        ch <- i + 5
    }
}
func suck(ch1, ch2 chan int) {
    for {
        select {
        case v := <-ch1:
            fmt.Printf("Received on channel 1: %d\n", v)
        case v := <-ch2:
            fmt.Printf("Received on channel 2: %d\n", v)
        }
    }
}

在程序 goroutine_select.go 中有 2 个通道 ch1ch2,三个协程 pump1()pump2()suck()。这是一个典型的生产者消费者模式。在无限循环中,ch1ch2 通过 pump1()pump2() 填充整数;suck() 也是在无限循环中轮询输入的,通过 select 语句获取 ch1ch2 的整数并输出。选择哪一个 case 取决于哪一个通道收到了信息。程序在 main 执行 1 秒后结束。


运行结果:

Received on channel 2: 148120
Received on channel 2: 148121
Received on channel 2: 148122
Received on channel 2: 148123
Received on channel 2: 148124
Received on channel 2: 148125
Received on channel 2: 148126
Received on channel 1: 296784
Received on channel 2: 148127
Received on channel 2: 148128
Received on channel 2: 148129
Received on channel 1: 296786
Received on channel 1: 296788


一秒内的输出非常惊人,如果我们给它计数(goroutine_select2.go),得到了 296788 个左右的数字。

相关文章
|
Go
select实战指南:协程切换技术
select实战指南:协程切换技术
144 0
|
Linux Go
学习golang(12) 初探:协程(3)多个chan之select选择器
学习golang(12) 初探:协程(3)多个chan之select选择器
360 0
|
缓存 监控 Linux
多进程、协程、事件驱动及select poll epoll
目录 -多线程使用场景 -多进程  --简单的一个多进程例子  --进程间数据的交互实现方法    ---通过Queues和Pipe可以实现进程间数据的传递,但是不能实现数据的共享    ---Queues     ---Pipe     ---通过Manager可以不同进程间实现数...
1367 0
|
4月前
|
Go 调度 Python
Golang协程和Python协程用法上的那些“不一样”
本文对比了 Python 和 Go 语言中协程的区别,重点分析了调度机制和执行方式的不同。Go 的协程(goroutine)由运行时自动调度,启动后立即执行;而 Python 协程需通过 await 显式调度,依赖事件循环。文中通过代码示例展示了两种协程的实际运行效果。
199 7
|
3月前
|
数据采集 网络协议 API
协程+连接池:高并发Python爬虫的底层优化逻辑
协程+连接池:高并发Python爬虫的底层优化逻辑
|
5月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
Go Python
使用python实现一个用户态协程
【6月更文挑战第28天】本文探讨了如何在Python中实现类似Golang中协程(goroutines)和通道(channels)的概念。文章最后提到了`wait_for`函数在处理超时和取消操作中的作
225 1
使用python实现一个用户态协程
|
调度 Python
python3 协程实战(python3经典编程案例)
该文章通过多个实战案例介绍了如何在Python3中使用协程来提高I/O密集型应用的性能,利用asyncio库以及async/await语法来编写高效的异步代码。
305 0
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
【7月更文挑战第15天】Python的协程与异步函数优化Web性能,通过非阻塞I/O提升并发处理能力。使用aiohttp库构建异步服务器,示例代码展示如何处理GET请求。异步处理减少资源消耗,提高响应速度和吞吐量,适用于高并发场景。掌握这项技术对提升Web应用性能至关重要。
280 10
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
【7月更文挑战第15天】Python 3.5+引入的协程和异步函数革新了并发编程。协程,轻量级线程,由程序控制切换,降低开销。异步函数是协程的高级形式,允许等待异步操作。通过`asyncio`库,如示例所示,能并发执行任务,提高I/O密集型任务效率,实现并发而非并行,优化CPU利用率。理解和掌握这些工具对于构建高效网络应用至关重要。
210 6