分享
  1. 首页
  2. 文章

Golang并发(五) - Select

90design · · 1695 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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 {}
}

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


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:90design

查看原文:Golang并发(五) - Select

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1695 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏