Go select 死锁引发的思考

上文总结

package main

import (
 "fmt"
)

func main() {
 ch := make(chan int)
 go func() {
  select {
  case ch getVal, i= 1
getVal, i= 2
package main

import (
 "fmt"
 "time"
)

func talk(msg string, sleep int)

每次进入以下 select 语句时:

select {
case ch

<-input1< code> &#x548C; <code><-input2< code> &#x90FD;&#x4F1A;&#x6267;&#x884C;&#xFF0C;&#x76F8;&#x5E94;&#x7684;&#x503C;&#x662F;&#xFF1A;A x &#x548C; B x&#xFF08;&#x5176;&#x4E2D; x &#x662F; 0-5&#xFF09;&#x3002;&#x4F46;&#x6BCF;&#x6B21; select &#x53EA;&#x4F1A;&#x9009;&#x62E9;&#x5176;&#x4E2D;&#x4E00;&#x4E2A; case &#x6267;&#x884C;&#xFF0C;&#x6240;&#x4EE5; <code><-input1< code> &#x548C; <code><-input2< code> &#x7684;&#x7ED3;&#x679C;&#xFF0C;&#x5FC5;&#x7136;&#x6709;&#x4E00;&#x4E2A;&#x88AB;&#x4E22;&#x5F03;&#x4E86;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;&#x4E0D;&#x4F1A;&#x88AB;&#x5199;&#x5165; ch &#x4E2D;&#x3002;&#x56E0;&#x6B64;&#xFF0C;&#x4E00;&#x5171;&#x53EA;&#x4F1A;&#x8F93;&#x51FA; 5 &#x6B21;&#xFF0C;&#x53E6;&#x5916; 5 &#x6B21;&#x7ED3;&#x679C;&#x4E22;&#x6389;&#x4E86;&#x3002;&#xFF08;&#x4F60;&#x4F1A;&#x53D1;&#x73B0;&#xFF0C;&#x8F93;&#x51FA;&#x7684; 5 &#x6B21;&#x7ED3;&#x679C;&#x4E2D;&#xFF0C;x &#x6BD4;&#x5982;&#x662F; 0 1 2 3 4&#xFF09;<!---input2<--></code><!---input1<--></code><!---input2<--></code><!---input1<-->

而 main 中循环 10 次,只获得 5 次结果,所以输出 5 次后,报死锁。

如果改为这样就一切正常:

select {
case t := <-input1: ch <- t case :="<-input2:" } < code></-input1:>

我的理解:
case ch <- <-input:< code>&#x8BED;&#x53E5;&#x662F;&#x5206;&#x6210;&#x4E24;&#x6BB5;&#x6267;&#x884C;&#x7684;&#xFF0C;&#x53EF;&#x4EE5;&#x7406;&#x89E3;&#x4E3A;<!----->

t := <- input case选择还未明确的时候会执行 ch <- t 如果没有选择此case,则不执行此语句 并且这是两条语句,具有先后顺序 所以<-input 执行后,没有选择此case,<-input的结果就会被丢弃掉,从而导致上述的死锁问题。 < code></->
getVal, i= 1
getVal, i= 2

思考一:如果getVal()方法执行的时间不同,select的运行时长是取决于运行时间长的,还是时间的总和?

func getVal1(i int) int {
    time.Sleep(time.Second * 1)
    fmt.Println("getVal, i=", i)
    return i
}
func getVal2(i int) int {
    time.Sleep(time.Second * 2)
    fmt.Println("getVal, i=", i)
    return i
}

func main() {
    ch := make(chan int)
    go func() {
        for {
            beginTime := time.Now()
            select {
            case ch

输出的结果

getVal, i= 1
getVal, i= 2
3.0015862s
getVal, i= 1
getVal, i= 2
3.0021938s
getVal, i= 1
getVal, i= 2
3.0019246s

可以看出来,每次select都会按顺序执行case语句,并且select的执行时间为case语句的总和
当然在实际生产中也不会有这种写法
正确的写法:

func main() {
    begin := time.Now()
    ch := make(chan int)
    ch2 := make(chan int, 2)
    go func() {
        ch2

输出结果,此时取决于运行时间最长的 getVal()

getVal, i= 1
1
getVal, i= 2
2
2.0020979s

在实际生产中,select语句只用于接受channel中的数值,而不是去执行某一方法

细心的小伙伴已经发现了,上述的写法有两个bug

加点注释看看输出的结果

func main() {
    begin := time.Now()
    ch := make(chan int)
    ch2 := make(chan int, 2)
    go func() {
        ch2

输出的结果

getVal, i= 1
getVal, i= 2
goroutine num 2
1
2
2.0020965s
goroutine num 2
panic err send on closed channel

可以看到,for循环的协程并没有被释放,并且在后续的 ch <-< code>&#x64CD;&#x4F5C;&#x4E2D;&#x4E5F;&#x62A5;&#x51FA;&#x4E86;panic&#x5F02;&#x5E38;<!---<-->

Original: https://www.cnblogs.com/xiaofua/p/15954405.html
Author: 小傅啊
Title: Go select 死锁引发的思考

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516418/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【golang】分布式缓存 – lru算法实现

    最近复习操作系统,看到了lru算法,就去网上搜索下,因此发现了GeeCache,顺手写了一遍。研究下lru算法的实现。 正文: lru使用map+链表实现。map里面存储了key以…

    Go语言 2023年5月25日
    048
  • Badger简单使用

    badger 是 dgraph 开源的 LSMTree 的 KV 引擎,它相比 leveldb 有 KV 分离、事务、并发合并等增强,是 go 生态中比较生产级的存储引擎了。 要开…

    Go语言 2023年5月25日
    075
  • GO语言 文件操作实例

    package main import ( "bufio" "fmt" "io/ioutil" "os&quo…

    Go语言 2023年5月29日
    056
  • 基于LSM的Key-Value数据库实现WAL篇

    上篇文章简单的实现了基于LSM数据库的初步版本,在该版本中如数据写入到内存表后但还未持久化到SSTable排序字符串表,此时正好程序崩溃,内存表中暂未持久化的数据将会丢失。 引入W…

    Go语言 2023年5月25日
    065
  • 记一次提升18倍的性能优化

    背景 最近负责的一个自研的 Dubbo 注册中心经常收到 CPU 使用率的告警,于是进行了一波优化,效果还不错,于是打算分享下思考、优化过程,希望对大家有一些帮助。 自研 Dubb…

    Go语言 2023年5月25日
    079
  • Go微服务框架go-kratos学习05:分布式链路追踪 OpenTelemetry 使用

    一、分布式链路追踪发展简介 1.1 分布式链路追踪介绍 关于分布式链路追踪的介绍,可以查看我前面的文章 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习(ht…

    Go语言 2023年5月25日
    054
  • go语言调用everything的SDK接口

    介绍 官方SDK地址 本项目会将官方dll编译到可执行程序中,运行时无需考虑dll问题。 根据官方介绍,使用SDK前需要运行 everything程序。 执行 go build -…

    Go语言 2023年5月25日
    083
  • gofs使用教程-基于golang的开源跨平台文件同步工具

    gofs是基于golang开发的一款开箱即用的跨平台文件同步工具,支持在本地磁盘之间同步、从远程服务器同步变更到本地、将本地文件变更推送到远程服务器三种模式。开源地址如下:Gith…

    Go语言 2023年5月25日
    057
  • sync:二. 延迟初始化(once)

    sync.Once 是 Go 标准库提供的使函数只执行一次的实现。作用与 init 函数类似,但有区别。在某些情况下预先初始化一个变量会增加函数的启动延迟,如果实际执行时可能用不上…

    Go语言 2023年5月25日
    079
  • gRPC in ASP.NET Core 3.x — Protocol Buffer(2)Go语言的例子(上)

    在GOPATH的src下面建立一个文件夹 protobuf-go,然后在里面执行命令 go mod init github.com/solenovex/protobuf-go 这个…

    Go语言 2023年5月29日
    053
  • 使用go语言遇到的一些问题记录

    一、参数校验问题 使用go做web服务时,经常需要对请求参数进行校验,有些必填参数需要校验是否为空。 经常会遇到参数a为int类型,但是其值取值范围为0-xxx。0也是有意义的。 …

    Go语言 2023年5月29日
    066
  • GO语言常用时间工具类 timeUtil.go

    package util import “time” /*获取时间戳(纳秒)Testner 20210123/func GetTime_UnixNano()…

    Go语言 2023年5月29日
    077
  • 雅可比行列式迭代及优化(golang版)

    最近遇到的一个求解雅可比迭代的问题,这个计算方法在 python 中有现成的库,但是在 golang 中没找到相应的实现。于是根据雅可比行列式的推导实现了一个 golang 版本的…

    Go语言 2023年5月25日
    064
  • Go 语言快速开发入门

    需求 开发的步骤 linux下如何开发Go程序 MAC下如何开发Go程序 Golang执行流程分析 编译和运行说明 Go程序开发的注意事项 Go语言的转义字符(escapechar…

    Go语言 2023年5月25日
    082
  • go-micro使用Consul做服务发现的方法和原理

    go-micro v4默认使用mdns做服务发现。不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul。…

    Go语言 2023年5月25日
    092
  • 系统调用跟踪——分析(一)

    通过strace工具可跟踪用户进程与Linux内核的调用交互,可看到其中的System Call(系统调用)情况; &#x5B89;&#x88C5;strace&a…

    Go语言 2023年5月25日
    098
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球