Golang并发(五) - Select

原创
2018/03/16 10:54
阅读数 1.5K

What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.

你所浪费的今天是昨天死去的人奢望的明天; 你所厌恶的现在是未来的你回不去的曾经。

 

select介绍

    select语句用于从多个发送/接收通道操作中进行选择。直到发送/接收操作之一准备就绪,select停止阻塞。如果多个操作准备就绪,随机选择其中一个。语法与switch类似,只是每个case语句都是一个通道操作。

    让我们直接进入一些代码,以便更好地理解。

package main

import (
	"fmt"
	"time"
)

func server1(ch chan string) {
	time.Sleep(2 * time.Second)
	ch <- "from server1"
}
func server2(ch chan string) {
	time.Sleep(1 * time.Second)
	ch <- "from server2"

}
func main() {
	output1 := make(chan string)
	output2 := make(chan string)
	go server1(output1)
	go server2(output2)
	select {
	case s1 := <-output1:
		fmt.Println(s1)
	case s2 := <-output2:
		fmt.Println(s2)
	}
	
	// time.Sleep(4*time.Second)
}

解释: main程启动两个goroutine来向通道中发送数据,select负责监听(一直处于阻塞状态),等待其中 一个goroutine发送数据成功,select 立即停止阻塞并接收数据后退出select。

试想:如果我们将代码中的注释部分开启,程序的输出内容有没有变化?

防止阻塞或default

当case没有准备好(获取到数据)时, default语句会执行,这样就防止了select的阻塞状态出现。

package main

import (
	"fmt"
	"time"
)
func process(in chan string) {
	for {
		time.Sleep(4 * time.Second)
		in <- "数据写入"
	}
}

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

	go process(in)
	for{
		time.Sleep(2*time.Second)
		select{
			case str := <-in :
				fmt.Println("接受到数据:",str)
		default:
			fmt.Println("等待中...")
		}
	}

}

    当mian程启动一个goroutine向通道中写数据后, main程开始for监听in 通道, sleep 2秒后, <-in 未能读取到数据,select 打印default内的数据, 等到再次sleep后in通道传递数据。

 

死锁

当没有数据发送时,仅仅从通道中来接收数据,程序将会永久的阻塞, 因此会导致死锁。

func main() {  
    ch := make(chan string)
    select {
    case <-ch:
    }
}

select从nil channel中接收数据

package main

import "fmt"

func main() {
	var ch chan string
	select {
	case v := <-ch:
		fmt.Println("received value", v)
	default:
		fmt.Println("default case executed")

	}
}

总结: select 中default语句是默认执行, 防止程序的阻塞产生。

Random Select

如果case语句中有多个通道发送来数据, 那select将会如何输出?

package main

import (  
    "fmt"
    "time"
)

func server1(ch chan string) {  
    ch <- "from server1"
}
func server2(ch chan string) {  
    ch <- "from server2"

}
func main() {  
    output1 := make(chan string)
    output2 := make(chan string)
    go server1(output1)
    go server2(output2)
    time.Sleep(2 * time.Second)  // 使所有goroutine的channel写入数据
    select {
    case s1 := <-output1:
        fmt.Println(s1)
    case s2 := <-output2:
        fmt.Println(s2)
    }
}

select将会随机的从中选择输出, 因此输出结果可能不相同。

空select

我们上面说到, select直到case(在没有default的前提下)中接收到数据时,才会停止阻塞。

那么空select会发生什么?

package main

func main() {  
    select {}
}

程序永久性阻塞(引发死锁)。

 

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部