记一次goroutine的死锁记录以及解决方法

tomleoken · 2019-07-23 20:50:57 · 1553 次点击 · 预计阅读时间 2 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2019-07-23 20:50:57 的文章,其中的信息可能已经有所发展或是发生改变。

最近开发需要无限循环某个任务,任务里要执行一段代码n次以获取相应的信息,然后合并处理,顺序下来的话时间就是o(n)了,然后go最大的特点不久是强大的并发么,所以想用goroutine来试试水,不料遭遇死锁了,第一次任务循环没问题,但是第二次任务循环就死锁了。

逻辑是,任务里(无限循环里),开8个协程分别执行8个代码块,然后代码块每执行完一次,slice就+1 。主程再开启一个协程,无线循环检测slice长度,只要长度=8,表示代码块执行完毕,chan释放。

代码如下

package main

import(
    "log"
    "time"
    "sync"
)

func main(){

    for {
        //var wg sync.WaitGroup
        var teststr []string 
        for i := 0; i<8; i++ {
            //wg.Add(1)
            go func(t int){
                // 隔 n 秒执行一次
                //defer wg.Done()
                log.Println("协程 ",t,"开启")
                time.Sleep(time.Duration(5)*time.Second)
                teststr = append(teststr,"haha")
                log.Println("协程 ",t," 存值完毕")
            }(i)
        }

        log.Println("循环结束")
        //wg.Wait()

        channel := make(chan int)

        go func(){
            log.Println("开启判断线程")
            for {
                if len(teststr) == 8 {
                    <- channel // 当数组长度与GPU数里相等,即上面的goroutine全部执行完毕
                    log.Println("通道释放")
                    break
                }
            }            
        }()

        log.Println("通道存值,主线程阻塞")
        channel <- 1 // 存值,当前线程(main)在此处阻塞
        log.Println(teststr)
    }

}

当然最后还是解决了实现了要求,解决方法代码如下

package main

import(
    "log"
    "time"
    "sync"
)

func main(){

    for {
        var wg sync.WaitGroup
        var teststr []string 
        for i := 0; i<8; i++ {
            wg.Add(1)
            go func(t int){
                // 隔 n 秒执行一次
                defer wg.Done()
                log.Println("协程 ",t,"开启")
                time.Sleep(time.Duration(5)*time.Second)
                teststr = append(teststr,"haha")
                log.Println("协程 ",t," 存值完毕")
            }(i)
        }

        log.Println("循环结束")
        wg.Wait()
        /*
        channel := make(chan int)

        go func(){
            log.Println("开启判断线程")
            for {
                if len(teststr) == 8 {
                    <- channel // 当数组长度与GPU数里相等,即上面的goroutine全部执行完毕
                    log.Println("通道释放")
                    break
                }
            }            
        }()

        log.Println("通道存值,主线程阻塞")
        channel <- 1 // 存值,当前线程(main)在此处阻塞
        */
        log.Println(teststr)
    }

}

其实代码就是上一篇把sync包的东西去掉注释,然后把channel相关的注释

虽然解决了,但是还不清楚为何第一段代码为何会死锁,逻辑过了好几遍了,自觉没问题,大家如果发现bug谢谢留言下哈


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1553 次点击  
加入收藏 微博
4 回复  |  直到 2019-07-31 21:08:40
EndlessCheng
EndlessCheng · #1 · 6年之前

对 teststr 的赋值不是原子操作,需要加锁

tomleoken
tomleoken · #2 · 6年之前
EndlessChengEndlessCheng #1 回复

对 teststr 的赋值不是原子操作,需要加锁

懂了,谢谢哈哈!!

cainiaofei
cainiaofei · #3 · 6年之前

第二段代码也没对赋值操作加锁,为什么没问题呢

tomleoken
tomleoken · #4 · 6年之前
cainiaofeicainiaofei #3 回复

第二段代码也没对赋值操作加锁,为什么没问题呢

这不太清楚哈。可能sync包有做锁操作吧。测试的时候的确没问题。

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传