select 多路复用

目录

一、循环接收多个信道的问题

二、select详解


一、循环接收多个信道的问题

在使用通道时,想同时接收多个通道的数据是一件困难的事情。通道在接收数据时,如果没有数据可以接收将会发生阻塞。

虽然可以使用如下模式进行遍历,但运行性能会非常差。

for{
    // 尝试接收ch1通道
    data, ok := <-ch1
    // 尝试接收ch2通道
    data, ok := <-ch2
    // 接收后续通道
    …
}

二、select详解

        Go 语言中提供了 select 关键字,可以同时响应多个通道的操作。select 的每个 case 都会对应一个通道的收发过程。当收发完成时,就会触发 case 中响应的语句。多个操作在每次 select 中挑选一个进行响应。

select{
    case 操作1:
        响应操作1
    
    case 操作2:
        响应操作2
    …
    default: //可选操作,fallthrough 行为,和普通的 switch 相似,是不允许的。
        没有操作情况
}

操   作

语句示例

接收任意数据

case <- ch:

接收变量

case d :=  <- ch:

发送数据

case ch <- 100:

select 做的就是:选择处理列出的多个通信情况中的一个,有如下说明:

  • 在任何一个 case 中执行 break 或者 return,select 就结束了
  • 如果都阻塞了,会等待直到其中一个可以处理
  • 如果多个可以处理,随机选择一个
  • 如果没有通道操作可以处理并且写了 default 语句,它就会执行:default 永远是可运行的(这就是准备好了,可以执行)。在 select 中使用发送操作并且有 default 可以确保发送不被阻塞!如果没有 default,select 就会一直阻塞

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

//select  通道的多路复用
package main

import (
   "fmt"
   "time"
)

//往通道中写数据pump1
func pump1(ch chan int){
   for i:=1;; i++{
      ch <- i*2
   }
}

//往通道中写数据pump2
func pump2(ch chan int){
   for i:=1; ; i++{
      ch <- i+2
   }
}

//接收通道中的数据
func suck(ch1, ch2 chan int)  {
   for{
      select{
         case v:= <- ch1:
            fmt.Println("received on channel 1:",v)
         case v2:= <-ch2:
            fmt.Println("received on channel 2:", v2)
      }
   }
}

func main(){
   ch1 := make(chan int)
   ch2 := make(chan int)
   go pump1(ch1)
   go pump2(ch2)
   go suck(ch1, ch2)
    
   time.Sleep(time.Second)
   
}

输出:
Received on channel 2: 5
Received on channel 2: 6
Received on channel 1: 0
Received on channel 2: 7
Received on channel 2: 8
Received on channel 2: 9
Received on channel 2: 10
Received on channel 1: 2
Received on channel 2: 11
...
Received on channel 2: 47404
Received on channel 1: 94346
Received on channel 1: 94348

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

猜你喜欢

转载自blog.csdn.net/demored/article/details/124147633