【容易踩坑】golang中select

76 阅读2分钟

1 select简介

在Go语言中,select语句用于在多个通道操作中进行选择。它可以同时等待多个通道操作,并在其中任何一个通道就绪时执行相应的操作。 select语句的语法如下:

select {
case <-channel1:
    // 执行channel1就绪时的操作
case data := <-channel2:
    // 执行channel2就绪时的操作,并将接收到的数据赋值给data变量
case channel3 <- data:
    // 执行channel3就绪时的操作,并将data发送到channel3
default:
    // 当没有任何通道操作就绪时执行的操作
}

select语句中的每个case语句都是一个通道操作,可以是接收操作、发送操作或者空操作。当select语句执行时,它会按照顺序检查每个case语句,如果其中任何一个通道操作就绪(即通道可以读取或写入),则执行相应的操作。如果有多个通道操作同时就绪,则随机选择一个执行。

select语句还可以包含一个default语句,用于在没有任何通道操作就绪时执行默认操作。default语句是可选的,如果没有提供,则当所有通道操作都没有就绪时,select语句会阻塞,直到至少有一个通道操作就绪。

select语句可以用于解决多个通道操作的并发问题,常见的应用场景包括超时控制、多路复用和取消操作等。

2 具体例子

使用select语句实现了一个简单的聊天室程序:

package main

import (
	"fmt"
	"os"
	"os/signal"
	"time"
)

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

	// 启动一个goroutine接收用户输入
	go func() {
		for {
			var input string
			fmt.Scanln(&input)
			ch1 <- input
		}
	}()

	// 启动一个goroutine模拟聊天室消息
	go func() {
		for {
			time.Sleep(2 * time.Second)
			ch2 <- "Hello, World!"
		}
	}()

	// 启动一个goroutine监听信号
	go func() {
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, os.Interrupt)
		<-sig
		done <- true
	}()

	for {
		select {
		case msg := <-ch1:
			fmt.Println("收到用户输入:", msg)
		case msg := <-ch2:
			fmt.Println("收到聊天室消息:", msg)
		case <-done:
			fmt.Println("程序退出")
			return
		}
	}
}

在上面的例子中,我们创建了两个通道ch1ch2,一个用于接收用户输入,一个用于接收聊天室消息。然后启动了三个goroutine,分别用于接收用户输入、模拟聊天室消息和监听信号。

select语句中,我们使用三个case语句分别处理用户输入、聊天室消息和程序退出信号。当用户输入时,会打印收到的用户输入;当收到聊天室消息时,会打印收到的消息;当收到程序退出信号时,会打印"程序退出"并返回。

通过使用select语句,我们可以同时等待多个通道操作,并在其中任何一个操作就绪时执行相应的操作,从而实现了一个简单的聊天室程序。