【go 】 select case 的用法

发布于:2024-12-07 ⋅ 阅读:(119) ⋅ 点赞:(0)


相关文章:
【go】select 语句case的随机性
【go】 select case 超时机制(time.After)示例和内存泄漏

1. 基本使用:监听多个通道,会阻塞

特点:
1)case 只能一次性的,一旦响应后,就结束了,参见后面第三章节for的例子,可以多次监听
2)如果多个通道都没有响应,会阻塞,直到某个通道率先响应

package main

import (
	"fmt"
	"time"
)

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

	// 启动两个 goroutine 发送数据
	go func() {
		time.Sleep(2 * time.Second)
		ch1 <- "Message from ch1"
	}()

	go func() {
		time.Sleep(1 * time.Second)
		ch2 <- "Message from ch2"
	}()

	// 使用 select 等待任意一个通道的消息
	select {
	case msg1 := <-ch1:
		fmt.Println("Received:", msg1)
	case msg2 := <-ch2:
		fmt.Println("Received:", msg2)
	}
}


输出示例:


Received: Message from ch2

2.带默认分支:非阻塞操作

select 可以配合 default 分支进行非阻塞操作。

当其他的case不满足条件时,就一定会走到default分支,不会阻塞


package main

import "fmt"

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

	select {
	case msg := <-ch:
		fmt.Println("Received:", msg)
	default:
		fmt.Println("No message received, doing something else...")
	}
}

输出:

No message received, doing something else...

3. 永远监听多个通道

用 for-select 模式可以实现对多个通道的持续监听。


package main

import (
	"fmt"
	"time"
)

/*
*
模拟一直监听
*/
func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		for {
			time.Sleep(2 * time.Second)
			ch1 <- "Ping"
		}
	}()

	go func() {
		for {
			time.Sleep(3 * time.Second)
			ch2 <- "Pong"
		}
	}()

	for {
		select {
		case msg := <-ch1:
			fmt.Println("Received from ch1:", msg)
		case msg := <-ch2:
			fmt.Println("Received from ch2:", msg)
		}
	}
}

4. 超时机制

详情参见 【go】 select case 超时机制(time.After)示例和内存泄漏

配合 time.After 可以实现超时控制。


package main

import (
	"fmt"
	"time"
)

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

	// 启动一个 goroutine,模拟长时间处理
	go func() {
		time.Sleep(3 * time.Second)
		ch <- "Result"
	}()

	select {
	case res := <-ch:
		fmt.Println("Received:", res)
	case <-time.After(2 * time.Second):
		fmt.Println("Timeout!")
	}
}

输出:

Timeout!

5. 关闭通道的处理

当通道关闭时,select 中接收操作会立即返回零值。

注意:当通道被关闭后,不是阻塞的,而是立即返回一个0值。 这里的0值是指长度为0的字符串,不是数字0


package main

import "fmt"

func main() {
	ch := make(chan int)
	go func() {
		ch <- 42
		close(ch)
	}()

	select {
	case val, ok := <-ch:
		if !ok {     //利用ok来判断是否通道是关闭的
			fmt.Println("Channel closed!")
		} else {
			fmt.Println("Received:", val)
		}
	}
}

6. context的关闭判断

有时候利用上下文,我们可以实现一些功能,并且需要判断上下文是否关闭,也可以通过select case



package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context, id int) {
	select {
	case <-ctx.Done():
		fmt.Printf("Worker %d: canceled\n", id)
	case <-time.After(2 * time.Second):
		fmt.Printf("Worker %d: completed\n", id)
	}
}

func main() {
	// 创建父 context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 子 context
	subCtx1, cancel1 := context.WithTimeout(ctx, 1*time.Second)
	defer cancel1()

	subCtx2, cancel2 := context.WithTimeout(ctx, 3*time.Second)
	defer cancel2()

	go worker(subCtx1, 1)
	go worker(subCtx2, 2)

	// 模拟某种条件触发取消
	time.Sleep(2 * time.Second)
	cancel() // 取消父 context,影响所有子 context

	time.Sleep(2 * time.Second)
	fmt.Println("Main function exits")
}


网站公告

今日签到

点亮在社区的每一天
去签到