15分钟学 Go 第 29 天:流程控制 - select语句

第29天:流程控制 - Select语句

1. 目标

理解Go语言中select语句的使用,以及如何在并发编程中有效地管理多个通道的操作。

2. select语句概述

select语句是Go语言中处理多个通道操作的强大机制。它类似于switch语句,但其用于处理通道事件。通过select,我们可以等待多个通道中的任意一个变得可用,从而实现高效的并发控制。

2.1 语法

go 复制代码
select {
case <-ch1:
    // ch1 可读取
case msg := <-ch2:
    // 从 ch2 可读取消息 msg
case ch3 <- msg:
    // 向 ch3 发送 msg
default:
    // 如果没有任何通道准备好
}

2.2 关键点

  • 每个case必须是一个通道操作。
  • select会随机选择一个可用的case,如果多个case同时可用,则会随机选择一个执行。
  • 若所有通道都不可用,且存在default分支,则执行default分支。
  • select语句是阻塞的,直到有case可以执行。

3. 使用场景

  • 处理多种通道: 允许并发地处理多个通道。
  • 超时控制: 在没有通道可用时,设置超时机制。
  • 任务结果收集: 收集多个并行任务的结果。

4. 示例代码

4.1 基本示例

下面是一个简单的select语句示例,展示如何从多个通道中接收数据。

go 复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "来自通道1的消息"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "来自通道2的消息"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("接收到:", msg1)
        case msg2 := <-ch2:
            fmt.Println("接收到:", msg2)
        }
    }
}

4.2 运行流程图

以下是基本示例的运行流程图:

markdown 复制代码
开始
  |
  v
创建通道 ch1 和 ch2
  |
  v
启动 goroutine 发送消息到 ch1
  |
  v
启动 goroutine 发送消息到 ch2
  |
  v
进入 for 循环
  |
  v
选择消息:
  |---------  ch1 有消息  -----------> 打印来自通道1的消息,继续
  |
  v
  |---------  ch2 有消息  -----------> 打印来自通道2的消息,继续
  |
  v
结束

5. 高级使用

5.1 超时控制

可以通过使用time.After函数来设置超时控制。

go 复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch <- "消息来自通道"
    }()

    select {
    case msg := <-ch:
        fmt.Println("接收到:", msg)
    case <-time.After(1 * time.Second):
        fmt.Println("超时,没有接收到消息")
    }
}

5.2 运行流程图

markdown 复制代码
开始
  |
  v
创建通道 ch
  |
  v
启动 goroutine,2秒后发送消息到 ch
  |
  v
进入 select 语句:
    |
  +---+------------------+
  |   |                  |
  v   v                  v
接收到消息     超时 1 秒     <--- 超时
  |   |                  |
  +---+------------------+
  |
  v
结束

5.3 选择多个通道的消息

在处理多个通道并选择第一个可用的情况下,可以更有效地使用select

go 复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "通道1的消息"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "通道2的消息"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("接收到:", msg1)
        case msg2 := <-ch2:
            fmt.Println("接收到:", msg2)
        }
    }
}

运行流程图:

markdown 复制代码
开始
  |
  v
创建通道 ch1 和 ch2
  |
  v
启动 goroutine 发送消息到 ch1
  |
  v
启动 goroutine 发送消息到 ch2
  |
  v
进入 for 循环
  |
  v
选择消息:
  |---------  ch1 有消息  -----------> 打印来自通道1的消息,继续
  |
  v
  |---------  ch2 有消息  -----------> 打印来自通道2的消息,继续
  |
  v
结束

6. 理解select的使用细节

6.1 战胜竞争条件

在并发编程中,需要小心避免竞争条件,尤其是在多个Goroutine同时访问共享资源时。select可以帮助我们合理安排资源使用。

6.2 哨兵模式

使用select可以实现哨兵模式,允许协程在通道中发送特定的结束信号。

go 复制代码
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)
    done := make(chan bool)

    go func() {
        for {
            select {
            case msg := <-ch:
                fmt.Println("接收到:", msg)
            case <-done:
                fmt.Println("接收任务完成,退出")
                return
            }
        }
    }()

    // 发送消息
    ch <- "Hello, Go!"
    time.Sleep(1 * time.Second)
    
    // 发送结束信号
    done <- true
}

流程图:

markdown 复制代码
开始
  |
  v
创建通道 ch 和 done
  |
  v
启动 goroutine 监听 ch 和 done
  |
  v
发送消息到 ch
  |
  v
等待 1 秒
  |
  v
发送结束信号到 done
  |
  v
  ------> goroutine 结束

7. 总结

通过本节内容,我们深入理解了Go语言的select语句及其在并发编程中的应用。这一机制为我们提供了灵活的方式来处理多个通道,提高程序的效率和响应性。

7.1 重要概念回顾

  • select语句用于监听多条通道的事件。
  • 在多个通道都可用时,select随机选择其中之一执行。
  • 可以通过time.After实现超时控制。
  • 哨兵模式能有效管理协程的结束信号。

8. 练习

  1. 写一个程序 ,使用select语句从两个通道中接收字符串,并记录哪个通道先接收到消息。
  2. 实现一个超时功能,如果在3秒内没有消息从通道中接收,则打印"超时"。
  3. 使用哨兵模式,当接收到特定消息时,结束程序。

通过以上内容,相信你对Go语言中的select语句有了全面的理解。如果在学习过程中有任何问题,欢迎随时讨论!


怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

相关推荐
cpp_25011 小时前
P1024 [NOIP 2001 提高组] 一元三次方程求解
数据结构·c++·算法·题解·二分答案·洛谷·csp
小羽网安1 小时前
从零开始学习 sql 注入,常见的 sql 注入解析
数据库·sql·学习
2401_846339562 小时前
CSS如何优化大型项目样式_使用SASS预处理器提升开发效率
jvm·数据库·python
Gofarlic_oms16 小时前
利用API实现ANSYS许可证管理自动化集成
运维·服务器·开发语言·matlab·自动化·负载均衡
AI+程序员在路上7 小时前
VS Code 完全使用指南:下载、安装、核心功能与 内置AI 编程助手实战
开发语言·人工智能·windows·开源
田梓燊7 小时前
力扣:23.合并 K 个升序链表
算法·leetcode·链表
invicinble8 小时前
这里对java的知识体系做一个全域的介绍
java·开发语言·python
小码哥_常8 小时前
MyBatis-Plus:让数据库操作飞起来的神器
后端
catchadmin8 小时前
使用 PHP TrueAsync 改造 Laravel 协程异步化的可行路径
开发语言·php·laravel
wbs_scy8 小时前
【Linux 线程进阶】进程 vs 线程资源划分 + 线程控制全详解
java·开发语言