文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么使用Go select语句实现高效并发

2023-07-05 12:17

关注

这篇文章主要介绍“怎么使用Go select语句实现高效并发”,在日常操作中,相信很多人在怎么使用Go select语句实现高效并发问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用Go select语句实现高效并发”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

select 介绍

什么是 select

selectGo 语言中的一种控制结构,用于在多个通信操作中选择一个可执行的操作。它可以协调多个 channel 的读写操作,使得我们能够在多个 channel 中进行非阻塞的数据传输、同步和控制。

为什么需要 select

Go 语言中的 select 语句是一种用于多路复用通道的机制,它允许在多个通道上等待并处理消息。相比于简单地使用 for 循环遍历通道,使用 select 语句能够更加高效地管理多个通道。

以下是一些 select 语句的使用场景:

等待多个通道的消息(多路复用)

当我们需要等待多个通道的消息时,使用 select 语句可以非常方便地等待这些通道中的任意一个通道有消息到达,从而避免了使用多个goroutine进行同步和等待。

超时等待通道消息

当我们需要在一段时间内等待某个通道有消息到达时,使用 select 语句可以与 time 包结合使用实现定时等待。

在通道上进行非阻塞读写

在使用通道进行读写时,如果通道没有数据,读操作或写操作将会阻塞。但是使用 select 语句结合 default 分支可以实现非阻塞读写,从而避免了死锁或死循环等问题。

因此,select 的主要作用是在处理多个通道时提供了一种高效且易于使用的机制,简化了多个 goroutine 的同步和等待,使程序更加可读、高效和可靠。

select 基础

语法

select {    case <- channel1:        // channel1准备好了    case data := <- channel2:        // channel2准备好了,并且可以读取到数据data    case channel3 <- data:        // channel3准备好了,并且可以往其中写入数据data    default:        // 没有任何channel准备好了}

其中, <- channel1 表示读取 channel1 的数据,data <- channel2 表示用 data 去接收数据;channel3 <- data 表示往 channel3 中写入数据。

select 的语法形式类似于 switch,但是它只能用于 channel 操作。在 select 语句中,我们可以定义多个 case,每个 case 都是一个 channel 操作,用于读取或写入数据。如果有多个 case 同时可执行,则会随机选择其中一个。如果没有任何可执行的 case,则会执行 default 分支(如果存在),或者阻塞等待直到至少有一个 case 可执行为止。

基本用法

package mainimport (   "fmt"   "time")func main() {   ch2 := make(chan int)   ch3 := make(chan int)   go func() {      time.Sleep(1 * time.Second)      ch2 <- 1   }()   go func() {      time.Sleep(2 * time.Second)      ch3 <- 2   }()   for i := 0; i < 2; i++ {      select {      case data, ok := <-ch2:         if ok {            fmt.Println("从 ch2 接收到数据:", data)         } else {            fmt.Println("通道已被关闭")         }      case data, ok := <-ch3:         if ok {            fmt.Println("从 ch3接收到数据: ", data)         } else {            fmt.Println("通道已被关闭")         }      }   }   select {   case data, ok := <-ch2:      if ok {         fmt.Println("从 ch2 接收到数据:", data)      } else {         fmt.Println("通道已被关闭")      }   case data, ok := <-ch3:      if ok {         fmt.Println("从 ch3接收到数据: ", data)      } else {         fmt.Println("通道已被关闭")      }   default:      fmt.Println("没有接收到数据,走 default 分支")   }}

执行结果

从 ch2 接收到数据: 1
从 ch3接收到数据:  2
没有接收到数据,走 default 分支

上述示例中,首先创建了两个 channelch2ch3,分别在不同的 goroutine 中向两个 channel 中写入数据。然后,在主 goroutine 中使用 select 语句监听两个channel,一旦某个 channel 上有数据流动,就打印出相应的数据。由于 ch2 中的数据比 ch3 中的数据先到达,因此首先会打印出 "从 ch2 接收到数据: 1",然后才打印出 "从 ch3接收到数据: 2"

为了方便测试 default 分支,我写了两个 select 代码块,执行到第二个 select 代码块的时候,由于 ch2ch3 都没有数据了,因此执行 default 分支,打印 "没有接收到数据,走 default 分支"

一些使用 select 与 channel 结合的场景

实现超时控制

package mainimport (   "fmt"   "time")func main() {   ch := make(chan int)   go func() {      time.Sleep(3 * time.Second)      ch <- 1   }()   select {   case data, ok := <-ch:      if ok {         fmt.Println("接收到数据: ", data)      } else {         fmt.Println("通道已被关闭")      }   case <-time.After(2 * time.Second):      fmt.Println("超时了!")   }}

执行结果为:超时了!

在这个例子中,程序将在 3 秒后向 ch 通道里写入数据,而我在 select 代码块里设置的超时时间为 2 秒,如果在 2 秒内没有接收到数据,则会触发超时处理。

实现多任务并发控制

package mainimport (   "fmt")func main() {   ch := make(chan int)   for i := 0; i < 10; i++ {      go func(id int) {         ch <- id      }(i)   }   for i := 0; i < 10; i++ {      select {      case data, ok := <-ch:         if ok {            fmt.Println("任务完成:", data)         } else {            fmt.Println("通道已被关闭")         }      }   }}

执行结果(每次执行的顺序都会不一致):

任务完成: 1
任务完成: 5
任务完成: 2
任务完成: 3
任务完成: 4
任务完成: 0
任务完成: 9
任务完成: 6
任务完成: 7
任务完成: 8

在这个例子中,启动了 10 个 goroutine 并发执行任务,并使用一个 channel 来接收任务的完成情况。在主函数中,使用 select 语句监听这个 channel,每当接收到一个完成的任务时,就进行处理。

监听多个通道的消息

package mainimport (   "fmt"   "time")func main() {   ch2 := make(chan int)   ch3 := make(chan int)   // 开启 goroutine 1 用于向通道 ch2 发送数据   go func() {      for i := 0; i < 5; i++ {         ch2 <- i         time.Sleep(time.Second)      }   }()   // 开启 goroutine 2 用于向通道 ch3 发送数据   go func() {      for i := 5; i < 10; i++ {         ch3 <- i         time.Sleep(time.Second)      }   }()   // 主 goroutine 从 ch2 和 ch3 中接收数据并打印   for i := 0; i < 10; i++ {      select {      case data := <-ch2:         fmt.Println("Received from ch2:", data)      case data := <-ch3:         fmt.Println("Received from ch3:", data)      }   }   fmt.Println("Done.")}

执行结果(每次执行程序打印的顺序都不一致):

Received from ch3: 5
Received from ch2: 0
Received from ch2: 1
Received from ch3: 6
Received from ch2: 2
Received from ch3: 7
Received from ch2: 3
Received from ch3: 8
Received from ch2: 4
Received from ch3: 9
Done.

该示例代码中,通过使用 select 多路复用,可以同时监听多个通道的数据,并避免了使用多个 goroutine 进行同步和等待的问题。

使用 default 实现非阻塞读写

import (   "fmt"   "time")func main() {   ch := make(chan int, 1)   go func() {      for i := 1; i <= 5; i++ {         ch <- i         time.Sleep(1 * time.Second)      }      close(ch)   }()   for {      select {      case val, ok := <-ch:         if ok {            fmt.Println(val)         } else {            ch = nil         }      default:         fmt.Println("No value ready")         time.Sleep(500 * time.Millisecond)      }      if ch == nil {         break      }   }}

执行结果(每次执行程序打印的顺序都不一致):

No value ready
1
No value ready
2
No value ready
No value ready
3
No value ready
No value ready
4
No value ready
No value ready
5
No value ready
No value ready

这个代码中,使用了 default 分支来实现非阻塞的通道读取和写入操作。在 select 语句中,如果有通道已经准备好进行读写操作,那么就会执行相应的分支。但是如果没有任何通道准备好读写,那么就会执行 default 分支中的代码。

select 的注意事项

以下是关于 select 语句的一些注意事项:

总之,在使用 select 语句时,要仔细考虑每个 case 语句的条件和执行顺序,避免死锁和其他问题。

到此,关于“怎么使用Go select语句实现高效并发”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯