V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
liu826250634
V2EX  ›  Go 编程语言

go 发生死锁的问题

  •  1
     
  •   liu826250634 · 2020-06-02 15:45:32 +08:00 · 2816 次点击
    这是一个创建于 1626 天前的主题,其中的信息可能已经有所发展或是发生改变。
    package main
    
    import (
    	"fmt"
    	"math/rand"
    	"sync"
    	"time"
    )
    
    var wg sync.WaitGroup
    
    //计算一个 64 位随机数的各位的和
    func randNumber(x int64) int64 {
    	var sum int64 = 0
    	for x > 0 {
    		a := x % 10
    		x = x / 10
    		sum += a
    	}
    	return sum
    }
    
    func main() {
    	wg.Add(25)
    	var jobChan = make(chan int64, 10)
    	var resultChan = make(chan int64, 10)
    	go func(jobChan chan<- int64, ) {
    		defer wg.Done()
    		for i:=0;i<1000;i++{
    			rand.Seed(time.Now().UnixNano())
    			jobChan <- rand.Int63n(100)
    		}
    	}(jobChan)
    
    	for i:=0;i<24;i++{
    		go func(jobChan <-chan int64, resultChan chan<- int64, ) {
    			defer wg.Done()
    			for num:= range jobChan{
    				resultChan <- randNumber(num)
    			}
    		}(jobChan, resultChan)
    	}
    
    	for i:= range resultChan{
    		fmt.Println(i)
    	}
    	wg.Wait()
    }
    
    
    发生错误:fatal error: all goroutines are asleep - deadlock!
    请教一下,为什么会发生死锁的情况?
    
    7 条回复    2020-06-03 17:58:10 +08:00
    linjunyi22
        1
    linjunyi22  
       2020-06-02 17:20:09 +08:00
    ```go
    package main

    import (
    "fmt"
    "math/rand"
    "sync"
    "time"
    )

    var wg sync.WaitGroup

    //计算一个 64 位随机数的各位的和
    func randNumber(x int64) int64 {
    var sum int64 = 0
    for x > 0 {
    a := x % 10
    x = x / 10
    sum += a
    }
    return sum
    }

    func main() {
    wg.Add(25)
    var jobChan = make(chan int64, 10)
    var resultChan = make(chan int64, 10)
    go func(jobChan chan<- int64, ) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
    rand.Seed(time.Now().UnixNano())
    jobChan <- rand.Int63n(100)
    }
    close(jobChan) // 此处要关闭 jobChan,否则在 jobChan 的 range 遍历中会一直阻塞
    }(jobChan)

    for i := 0; i < 24; i++ {
    go func(jobChan <-chan int64, resultChan chan<- int64, ) {
    defer wg.Done()
    for num := range jobChan {
    resultChan <- randNumber(num)
    }
    }(jobChan, resultChan)
    }
    // 此处 resultChan 的遍历放到一个 goroutine 中执行,让 wg 的 wait 执行完后主协程直接退出
    // 如果放在主协程中,也没有关闭 resultChan 的话,也会造成阻塞,就会产生死锁
    go func() {
    for i := range resultChan {
    fmt.Println(i)
    }
    }()
    wg.Wait()
    }
    ```
    beidounanxizi
        2
    beidounanxizi  
       2020-06-02 17:21:04 +08:00
    去看看 waitgroup 实现吧
    evill
        3
    evill  
       2020-06-02 17:36:57 +08:00
    channel 没有关闭
    for num:= range jobChan
    永远不会退出
    zjj19950716
        4
    zjj19950716  
       2020-06-02 17:39:22 +08:00
    for num:= range jobChan{
    resultChan <- randNumber(num)
    }
    job 1000 个发完之后,这边还一直卡主。
    最后的 Wait()也没意义,从 resultChan 读那里也退不出来
    asAnotherJack
        5
    asAnotherJack  
       2020-06-02 17:41:24 +08:00
    原因就是 2l 说的那样,因为 jobChan 和 resultChan 没有 close 阻塞在循环里了,jobChan 就像 2l 那样处理,resultChan 的处理,可以在 wg.Wait()后边 close resultChan,然后再 range resultChan 打印结果
    liu826250634
        6
    liu826250634  
    OP
       2020-06-03 17:39:51 +08:00
    @linjunyi22 感谢, 刚学习 go,有一些概念改搞不清。但是你这种方法好像会造成接收不完全就会退出。我自己也解决了这个问题了。

    ```
    package main

    import (
    "context"
    "fmt"
    "math/rand"
    "sync"
    "time"
    )

    var (
    wg = sync.WaitGroup{} // 用于计数, 让程序正常执行,不会主函数执行完子函数还没执行。计数清 0 则不用等待
    a int
    //lock sync.Mutex
    rwlock sync.RWMutex
    icons map[string]string
    loadIconsOnce sync.Once
    //m sync.Map
    )

    func randNumber(x int64) int64 {
    var sum int64 = 0
    for x > 0 {
    a := x % 10
    x = x / 10
    sum += a
    }
    return sum
    }


    func main() {
    wg.Add(25)
    defer wg.Wait()
    var maxSend = 10
    var jobChan = make(chan int64, 10)
    var resultChan = make(chan int64, 10)
    var lock sync.Mutex
    ctx, cancel := context.WithCancel(context.Background())

    //var once sync.Once
    go func(jobChan chan<- int64, ) {
    for i:=0;i<maxSend;i++{
    rand.Seed(time.Now().UnixNano())
    jobChan <- rand.Int63n(100)
    }
    close(jobChan)
    wg.Done()
    }(jobChan)

    count1 := 1
    for i:=0;i<24;i++{
    go func(jobChan <-chan int64, resultChan chan int64, ctx context.Context) {
    defer wg.Done()
    for num:= range jobChan{
    select {
    case <- ctx.Done():
    return
    case resultChan <- randNumber(num):
    lock.Lock()
    fmt.Println("count:", count1)
    count1 += 1
    lock.Unlock()
    }

    }
    }(jobChan, resultChan, ctx)
    }

    num := maxSend
    count := 1
    for value := range resultChan {
    if num == 1 {
    fmt.Printf("key:%v, value:%v\n", count, value)
    cancel()
    return
    }else {
    num -= 1
    fmt.Printf("key:%v, value:%v\n", count, value)
    count += 1
    }
    }
    }

    ```
    liu826250634
        7
    liu826250634  
    OP
       2020-06-03 17:58:10 +08:00
    回复中, 写代码怎么 md 不生效了= =
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5595 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 01:37 · PVG 09:37 · LAX 17:37 · JFK 20:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.