当前位置: 首页 > news >正文

一个select死锁问题

以下代码的输出结果:

func main() {
	var wg sync.WaitGroup
	foo := make(chan int)
	bar := make(chan int)
	wg.Add(1)
	go func() {
		defer wg.Done()
		select {
		case foo <- <-bar:
		default:
			println("default")
		}
	}()
	wg.Wait()
}

结果

在这里插入图片描述

解析

  1. 对于 select 语句,在进入该语句时,会按源码的顺序对每一个 case 子句进行求值:这个求值只针对发送或接收操作的额外表达式。将代码改为:

    func main() {
    	var wg sync.WaitGroup
    	foo := make(chan int)
    	bar := make(chan int)
    	wg.Add(1)
    	go func() {
    		defer wg.Done()
    		select {
    		case val:=<-bar:
    			foo<-val
    		// case foo <- <-bar:
    		default:
    			println("default")
    		}
    	}()
    	wg.Wait()
    }
    

    此时,正常输出:default

  2. 所以上述语句的理解就是:getVal()、<-input 和 getch() 等类似的操作会执行,导致死锁

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	ch := make(chan int)
    	go func() {
    		select {
    		case ch <- getVal(1):
    			fmt.Println("in first case")
    		case ch <- getVal(2):
    			fmt.Println("in second case")
    		default:
    			fmt.Println("default")
    		}
    	}()
    
    	fmt.Println("The val:", <-ch)
    }
    
    func getVal(i int) int {
    	fmt.Println("getVal, i=", i)
    	return i
    }
    

    无论 select 最终选择了哪个 case,getVal() 都会按照源码顺序执行:getVal(1) 和 getVal(2),必然输出:getVal, i= 1 | getVal, i= 2

相同问题

为什么每次都是输出一半数据,然后死锁?

package main

import (
	"fmt"
	"time"
)

func talk(msg string, sleep int) <-chan string {
	ch := make(chan string)
	go func() {
		for i := 0; i < 5; i++ {
			ch <- fmt.Sprintf("%s %d", msg, i)
			time.Sleep(time.Duration(sleep) * time.Millisecond)
		}
	}()
	return ch
}

func fanIn(input1, input2 <-chan string) <-chan string {
	ch := make(chan string)
	go func() {
		for {
			select {
			case ch <- <-input1:
			case ch <- <-input2:
			}
		}
	}()
	return ch
}

func main() {
	ch := fanIn(talk("A", 10), talk("B", 1000))
	for i := 0; i < 10; i++ {
		fmt.Printf("%q\n", <-ch)
	}
}
  1. 每次进入以下 select 语句时:<-input1 和 <-input2 都会执行,相应的值是:A x 和 B x(其中 x 是 0-5)。但每次 select 只会选择其中一个 case 执行,所以 <-input1 和 <-input2 的结果,必然有一个被丢弃了,也就是不会被写入 ch 中。因此,一共只会输出 5 次,另外 5 次结果丢掉了。
  2. 而 main 中循环 10 次,只获得 5 次结果,所以输出 5 次后,报死锁。

更多类似的问题

// ch 是一个 chan int;
// getVal() 返回 int
// input 是 chan int
// getch() 返回 chan int
select {
  case ch <- getVal():
  case ch <- <-input:
  case getch() <- 1:
  case <- getch():
}

最典型的问题

有内存泄露(传递给 time.After 的时间参数越大,泄露会越厉害)

PS: 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

func main()  {
    ch := make(chan int, 10)

    go func() {
        var i = 1
        for {
            i++
            ch <- i
        }
    }()

    for {
        select {
        case x := <- ch:
            println(x)
        case <- time.After(30 * time.Second):
            println(time.Now().Unix())
        }
    }
}
  1. 每次执行select都会执行一次time.After(30*time.Second)获取一个chan。

相关文章:

  • 青岛网站建设服务/百度导航最新版本
  • 宣城做网站/女生seo专员很难吗为什么
  • 做网站的回扣/广东seo网站设计
  • 学做网站需要懂什么软件/惠州短视频seo
  • 湘潭县建设投资有限公司网站/营销策划精准营销
  • 跨境电商网站开发/适合女生去的培训机构
  • 试卷安全分发系统
  • 酒水销售网站
  • 【MQTT】mqtt + go + M5Stack + SSR 远程灯控程序
  • 3DEXPERIENCE平台2023新功能揭秘!Governance云端数据管理解决方案
  • Win11的两个实用技巧系列之玩游戏闪跳、错误代码0x80004005解决
  • 什么是字符编码,为什么要字符编码?
  • LinkedList api
  • pybind11学习 | 面向对象编程
  • -防火墙-
  • 寒假集训题目集二A - 产生冠军
  • Spring5框架总结学习(从入门到进阶)-AOP
  • vim的常规操作