go 多路复用

本文介绍了Go语言中的多路复用机制,通过示例展示了select如何在多个channel间选择,并解释了默认情况、死锁及空select的概念。在没有case准备就绪时,default子句将被执行,防止死锁。同时,当select为空时,程序将陷入死锁。文章还提供了一个检查channel是否已满的实用案例。
摘要由CSDN通过智能技术生成

go 多路复用

多路复用:select 语句用于在多个发送/接收信道操作中进行选择。select 语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select 会随机地选取其中之一执行。该语法与 switch 类似,所不同的是,这里的每个 case 语句都是信道操作。

func main() {
	var (
		ch1 = make(chan string)
		ch2 = make(chan string)
	)
	go test2(ch1)
	go test3(ch2)

	select {
	case res := <-ch1:
		fmt.Println(res)
	case res := <-ch2:
		fmt.Println(res)
	}
}

func test2(ch chan string)  {
	fmt.Println("我是test2")
    time.Sleep(3*time.Second)  // 等待两个goroutine都执行完
	ch <- "test2的数据"
}
func test3(ch chan string)  {
	fmt.Println("我是test3")
    time.Sleep(3*time.Second) // 等待两个goroutine都执行完
	ch <- "test3的数据"
}

上述示例代码,使用 select 等待信道中传递过来值,一旦其中一个信道接收到值就会运行 case 下的代码块。

所以,多次运行,结果不会一模一样,如果是同时多个信道中接收到值,会随机选择一个执行。

默认情况

在没有 case 准备就绪时,可以执行 select 语句中的默认情况(Default Case)。这通常用于防止 select 语句一直阻塞。

select {
    case res := <-ch1:
    fmt.Println(res)
    case res := <-ch2:
    fmt.Println(res)
    default:
    fmt.Println("default的情况")
    time.Sleep(time.Second)
}

在上方 select代码块中加入default后,因为 case 都没有准备就绪,因此会直接执行default下的代码块。

但是我们使用 default 一般都是配合 for 循环来使用。

count := 0
for {
    select {
        case res := <-ch1:
        fmt.Println(res)
        case res := <-ch2:
        fmt.Println(res)
        default:
        fmt.Println("default的情况")
        time.Sleep(time.Second)
    }
    count++
    if count > 10 {
        break
    }
}

死锁

因为 select 是和 channel 一起使用的,因此使用不当就有可能造成死锁现象。

func main() {
	ch := make(chan string)
	select {
	case <-ch:
		fmt.Println("死锁")
	}
}

但是加上 默认情况后就能避免死锁的发生, 因为没有 准备就绪 case,就会执行 default 下的代码块

func main() {
	ch := make(chan string)
	select {
	case <-ch:
		fmt.Println("死锁")
	default:
		fmt.Println("我是default")
	}
}

并且select 只含有值为 nil 的信道,也同样会执行默认情况。

func main() {
	var ch chan string  // channel 是引用类型,定义,值为nil
	select {
	case <- ch:
		fmt.Println("死锁")
	default:
		fmt.Println("我是default")
	}
}

空select

我们在前面已经知道,除非有 case 执行,select 语句就会一直阻塞着。在这里,select 语句没有任何 case,因此它会一直阻塞,导致死锁。

func main() {
	select {}
}

案例

判断信道中有没有存满

var wg1 sync.WaitGroup
func main() {

	output1 := make(chan string, 5)  // 长度为5的有缓冲信道
	// 子协程写数据
	wg1.Add(1)
	go write(output1)
	wg1.Wait()
}

func write(ch chan string) {
	for {
		select {
		case ch <- "hello":  // 往信道中写hello
			fmt.Println("write hello")
		default:
			fmt.Println("信道满了")
			wg1.Done()
			break  // 标准写法,要写break,整个goroutine就会结束,防止浪费资源
		}
		time.Sleep(time.Millisecond * 500)
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go&Python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值