V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
proxytoworld
V2EX  ›  问与答

go 为什么这样的代码会产生死锁问题

  •  
  •   proxytoworld · 2022-02-11 00:10:46 +08:00 · 1457 次点击
    这是一个创建于 798 天前的主题,其中的信息可能已经有所发展或是发生改变。

    按道理 main 结束之前启动的 goroutine 不会结束,如果不发送 stopCh ,则会一直阻塞,为啥会提示有死锁

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    var wg sync.WaitGroup
    
    func main() {
    	fmt.Print("hello world\n")
    	ch1 := make(chan int)
    	ch2 := make(chan int)
    	stopCh := make(chan struct{})
    	wg.Add(1)
    	go work(ch1, ch2, stopCh)
    	ch1 <- 1
    	ch2 <- 1
    	time.Sleep(1000 * time.Millisecond)
    	// stopCh <- struct{}{}
    	var a int
    
    	wg.Wait()
    	fmt.Scan(&a)
    }
    func work(ch1, ch2 chan int, stopCh <-chan struct{}) {
    	defer wg.Done()
    	for {
    		select {
    		case <-stopCh:
    			return
    		case job1 := <-ch1:
    			fmt.Println("receive ch1 on first loop:", job1)
    		case job2 := <-ch2:
    		priority:
    			for {
    				select {
    				case job1 := <-ch1:
    					fmt.Println("receive ch1 on second loop:", job1)
    				default:
    					time.Sleep(1000 * time.Millisecond)
    					break priority
    				}
    			}
    			// 执行 job1 的内容
    			fmt.Println("receive ch2:", job2)
    		}
    	}
    }
    
    
    

    运行之后报错

    hello world
    receive ch1 on first loop: 1
    receive ch2: 1
    fatal error: all goroutines are asleep - deadlock!
    
    goroutine 1 [semacquire]:
    sync.runtime_Semacquire(0xd77c28)
            C:/go/src/runtime/sema.go:56 +0x49
    sync.(*WaitGroup).Wait(0xd77c20)
            C:/go/src/sync/waitgroup.go:130 +0x6b
    main.main()
            F:/code/go/c2c/client/client.go:24 +0x1b6
    
    goroutine 6 [select]:
    main.work(0xc000014180, 0xc0000141e0, 0xc000014240)
            F:/code/go/c2c/client/client.go:30 +0x26e
    created by main.main
            F:/code/go/c2c/client/client.go:17 +0x146
    exit status 2
    
    
    11 条回复    2022-02-14 12:58:04 +08:00
    quzard
        1
    quzard  
       2022-02-11 00:32:53 +08:00 via Android
    你的 work 被阻塞休眠了。然后导致已有的 goroutine 都处于休眠状态,导致报错了
    quzard
        2
    quzard  
       2022-02-11 00:35:15 +08:00 via Android
    WaitGroup 永远不会被通知 done ,造成了异常
    lianyue
        3
    lianyue  
       2022-02-11 00:36:43 +08:00
    进了
    job2 := <-ch2
    就不能退出了
    永远都在里面
    CEBBCAT
        4
    CEBBCAT  
       2022-02-11 00:50:24 +08:00
    楼主的第一句话看得不是很懂,work 的第一个 for 也没看见能主动退出的地方啊
    ch2
        5
    ch2  
       2022-02-11 01:46:43 +08:00 via iPhone
    建议先把名字取好,没人愿意看一堆 c1 、c2 不知干啥用的变量名
    lovelylain
        6
    lovelylain  
       2022-02-11 08:19:35 +08:00 via Android
    stopCh <- struct{}{}被你注释了,main 阻塞在 wg.Wait(),work 阻塞在 select ,没其他 goroutine 在活动能改变这俩阻塞状态。
    awalkingman
        7
    awalkingman  
       2022-02-11 09:42:24 +08:00   ❤️ 2
    @ch2 你这昵称很应景哈哈哈哈哈
    proxytoworld
        8
    proxytoworld  
    OP
       2022-02-11 14:25:34 +08:00
    @CEBBCAT 在里面有 return 这个协程会退出 如果收到 stopCh
    proxytoworld
        9
    proxytoworld  
    OP
       2022-02-11 14:25:52 +08:00
    @ch2 测试用例 所以没取名
    proxytoworld
        10
    proxytoworld  
    OP
       2022-02-11 14:26:29 +08:00
    @jobmailcn 懂了
    CEBBCAT
        11
    CEBBCAT  
       2022-02-14 12:58:04 +08:00
    推荐楼主学习一下《银行家算法》
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3554 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 04:59 · PVG 12:59 · LAX 21:59 · JFK 00:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.