Golang关键字select的常用用法总结

 更新时间:2023年10月26日 09:58:56   作者:林欣快滚去学习  
这篇文章主要为大家详细介绍了golang中select关键字的常用用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

一、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.WatchChan
    if 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 = nil
			case 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 = &sc
				cc.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的基本实现;

以上就是Golang关键字select的常用用法总结的详细内容,更多关于go select的资料请关注脚本之家其它相关文章!

相关文章

  • Go源码分析之预分配slice内存

    Go源码分析之预分配slice内存

    这篇文章主要从Go语言源码带大家分析一下预分配slice内存的相关知识,文中的示例代码简洁易懂,对我们深入了解go有一定的帮助,需要的可以学习一下
    2023-08-08
  • Go 请求兔子识别接口实现流程示例详解

    Go 请求兔子识别接口实现流程示例详解

    这篇文章主要为大家介绍了Go 请求兔子识别接口实现流程示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • golang 使用sort.slice包实现对象list排序

    golang 使用sort.slice包实现对象list排序

    这篇文章主要介绍了golang 使用sort.slice包实现对象list排序,对比sort跟slice两种排序的使用方式区别展开内容,需要的小伙伴可以参考一下
    2022-03-03
  • 详解Golang如何比较两个slice是否相等

    详解Golang如何比较两个slice是否相等

    开发中常会遇到需要比较两个slice包含的元素是否完全相等的情况,我们通常会通过两种方法去比较切片是否相等。这里通过几个示例来看一下这两种方法,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助
    2022-11-11
  • go语言题解LeetCode1128等价多米诺骨牌对的数量

    go语言题解LeetCode1128等价多米诺骨牌对的数量

    这篇文章主要为大家介绍了go语言题解LeetCode1128等价多米诺骨牌对的数量示例详解,
    2022-12-12
  • 关于golang中map使用的几点注意事项总结(强烈推荐!)

    关于golang中map使用的几点注意事项总结(强烈推荐!)

    map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用,下面这篇文章主要给大家介绍了关于golang中map使用的几点注意事项,需要的朋友可以参考下
    2023-01-01
  • golang中GOPROXY如何设置

    golang中GOPROXY如何设置

    在Go语言开发中,合理设置 GOPROXY 环境变量可以显著提升依赖包的下载速度和稳定性,针对高级场景,可以配置多代理冗余、禁止代理访问特定包或关闭校验和验证,下面就来详细的介绍一下
    2026-02-02
  • Golang配置解析神器go viper使用详解

    Golang配置解析神器go viper使用详解

    viper是一个很完善的Go项目配置解决方案,很多著名的开源项目都在使用,比如Hugo,Docker都使用了该库,使用viper可以让我们专注于自己的项目代码,而不用自己写那些配置解析代码,本文给大家介绍Golang配置解析神器go viper使用,感兴趣的朋友一起看看吧
    2022-05-05
  • golang实现nacos获取配置和服务注册-支持集群详解

    golang实现nacos获取配置和服务注册-支持集群详解

    文章介绍了如何在Go语言中使用Nacos获取配置和服务注册,支持集群初始化,客户端结构体中的IpAddresses可以配置多个地址,新客户端支持集群配置,文章还讨论了如何将Nacos获取到的配置与项目中的Viper无缝对接,以便进行Viper的正常操作
    2025-11-11
  • Go语言实现的排列组合问题实例(n个数中取m个)

    Go语言实现的排列组合问题实例(n个数中取m个)

    这篇文章主要介绍了Go语言实现的排列组合问题,结合实例形式分析了Go语言实现排列组合数学运算的原理与具体操作技巧,需要的朋友可以参考下
    2017-02-02

最新评论