Golang关键字

编程入门 行业动态 更新时间:2024-10-28 20:27:48

Golang<a href=https://www.elefans.com/category/jswz/34/1769821.html style=关键字"/>

Golang关键字

一、Select解决什么问题?


在Golang中,两个协程之间通信Channel(图一),在接受协程中通过代码表示即为<ch;如果协程需要监听多个Channel,只要有其中一个满足条件,就执行相应的逻辑(图二),这种select的应用场景之一,代码如下:

func TestSelect(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int , 1)select {case <-ch1:fmt.Println("ch1")case <-ch2:fmt.Println("ch2")default:}
}

上述代码,创建两个通道,通过select监听协程是否有数据,如果有打印相应的值,如果没有通过default结束程序运行;

二、Select常用用法

循环阻塞监测

func TestLoopSelect(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int, 1)go func() {// 每隔1s发送一条消息到channel中for range time.Tick(1 * time.Second) {ch1 <- 1}}()for {select {case <-ch1:fmt.Println("ch1")case <-ch2:fmt.Println("ch2")}}
}

这种写法特别常见,起一个协程,阻塞循环监听多个channel,如果有数据执行对应的操作。比如说

// go-zero/core/discov/internal/registry.go
func (c *cluster) watchStream(cli EtcdClient, key string, rev int64) bool {var rch clientv3.WatchChanif rev != 0 {rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix(),clientv3.WithRev(rev+1))} else {rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix())}for {select {case wresp, ok := <-rch:...c.handleWatchEvents(key, wresp.Events)case <-c.done:return true}}
}

上述代码,通过for + select阻塞循环监测注册中心数据是否有变换,有变化的话,针对变化类型,执行对应逻辑;

非阻塞监控

func TestSelect(t *testing.T) {ch1 := make(chan int, 1)ch2 := make(chan int , 1)select {case <-ch1:fmt.Println("ch1")case <-ch2:fmt.Println("ch2")default:}
}

这段代码的意思是,程序执行到select,就检查一下channel里面是否有数据,有就处理,没有就退出;在grpc-go中,

// grpc-go/clientconn.go
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {cc := &ClientConn{target: target,conns:  make(map[*addrConn]struct{}),dopts:  defaultDialOptions(),czData: new(channelzData),}defer func() {select {case <-ctx.Done():switch {case ctx.Err() == err:conn = nilcase err == nil || !cc.dopts.returnLastError:conn, err = nil, ctx.Err()default:conn, err = nil, fmt.Errorf("%v: %v", ctx.Err(), err)}default:}}()if cc.dopts.scChan != nil {// Blocking wait for the initial service config.select {case sc, ok := <-cc.dopts.scChan:if ok {cc.sc = &sccc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc})}case <-ctx.Done():return nil, ctx.Err()}}}

上述代码中,有两个地方用到select,一个地方是非阻塞,检查contex是否被取消;另外一个是阻塞等待;

三、select原理


如果想了解select的实现,可以阅读runtime.selectgo代码。它主要包含三部分:

首先,检查一下是否有准备就绪的channel(多个channel就绪,随机选择一个),如果有,就执行;
其次,将当前goroutine包装成sudog,挂载到对应的channel上;
最后,如果channel中数据准备就绪,唤醒该协程继续执行第一步逻辑;

【注】源码细节,感兴趣并且有需求可以深入了解,但是不要陷入源码的怪圈中;

总结

本文主要讲述下面三部分内容:

从Go源码开发者的角度考虑,为什么需要select?
介绍了select常用的两种写法,一种是非阻塞的,一种是阻塞的,以及开源项目如何使用它们;
介绍了select的基本实现;

参考

Go语言设计与实现
gprc-go
go-zero

更多推荐

Golang关键字

本文发布于:2023-12-03 11:00:29,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1654617.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:关键字   Golang

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!