背景

项目中遇到死锁,使用搜索引擎搜索goroutine堆栈中出现的“sync.runtime_Semacquire deadlock”时,搜到一篇说sync.WaitGroup死锁,特此记录一下

当然项目中的问题不是这个导致的,是sync.RWMutex重入导致的

问题复现

func TestReLock(t *testing.T) {
var wg sync.WaitGroup
ch := make(chan int, 1000)
for i := 0; i < 1000; i++ {
wg.Add(1)
go doSomething(i, wg, ch)
}
wg.Wait()
fmt.Println("all done")
for i := 0; i < 1000; i++ {
dd := <-ch
fmt.Println("from ch:"+strconv.Itoa(dd))
}
} func doSomething(index int, wg sync.WaitGroup, ch chan int) {
defer wg.Done()
//fmt.Println("start done:" + strconv.Itoa(index))
//time.Sleep(20 * time.Millisecond)
ch <- index
}

分析

在golang中结构体传参是传值,而不是传引用,从而在doSomething函数中接收到的是sync.WaitGroup的一个复制,而不是主线程中定义的变量,所以大家都不是操作的同一个对象,因此主线程无法等到WaitGroup完成。

因此解决办法很简单,改成传引用即可,如下所示:

func doSomething(index int, wg *sync.WaitGroup, ch chan int)

noCopy介绍

在测试上面的错误使用示例的时候,会发现IDE在关键词sync.WaitGroup上显示高亮,并提示“'doSomething' passes lock by value: type 'sync.WaitGroup' contains 'interface{}' which is 'sync.Locker' ”,也就是说IDE其实提示了使用不正确。

仔细看sync.WaitGroup实现,会发现其中封装了一个noCopy对象

type WaitGroup struct {
noCopy noCopy // 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
// 64-bit atomic operations require 64-bit alignment, but 32-bit
// compilers do not ensure it. So we allocate 12 bytes and then use
// the aligned 8 bytes in them as state, and the other 4 as storage
// for the sema.
state1 [3]uint32
}

  noCopy也是一个结构体,并且实现了Locker接口

type noCopy struct{}

// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}

  所以在golang中,如果一个结构体希望禁止用户做值拷贝,可以在结构体中使用一个noCopy变量,golang中有sync.Cond/sync.waitGroup/sync.Pool中使用了noCopy。其实golang中是禁止Locker接口值拷贝,所以sync.Mutex和sync.RWMutex等等类型如果传值也会提示该错误。

golang sync 包中:
sync.Cond
sync.Pool
sync.WaitGroup
sync.Mutex
sync.RWMutex
- ……
禁止拷贝,

golang 没有禁止对实现sync.Locker接口的对象实例赋值进行报错,只是在使用go vet 做静态语法分析时,会提示错误。

参考资料

https://stackoverflow.com/questions/13566065/why-my-golang-channel-raise-dead-lock-error

sync.WaitGroup的使用以及坑

golang sync.WaitGroup错误使用导致死锁以及noCopy结构体介绍的更多相关文章

  1. Golang Sync.WaitGroup 使用及原理

    Golang Sync.WaitGroup 使用及原理 使用 func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.A ...

  2. Golang sync.WaitGroup的用法

    0x01 介绍 经常会看到以下了代码: 12345678910111213 package main import ( "fmt" "time") func m ...

  3. golang sync.WaitGroup

    //阻塞,直到WaitGroup中的所以过程完成. import ( "fmt" "sync" ) func wgProcess(wg *sync.WaitGr ...

  4. golang(07)结构体介绍

    golang支持面向对象的设计,一般支持面向对象的语言都会有class的设计,但是golang没有class关键字,只有struct结构体.通过结构体达到类的效果,这叫做大成若缺,其用不弊. stru ...

  5. golang sync.WaitGroup bug

    注意,这个结构体,要是想在函数之间传来传去的话,必须要使用指针....... 这个结构体里没有 指针,这个类型可以说没有“引用特性”. 被坑了一晚上.特此记录.

  6. golang-----golang sync.WaitGroup解决goroutine同步

    go提供了sync包和channel来解决协程同步和通讯.新手对channel通道操作起来更容易产生死锁,如果时缓冲的channel还要考虑channel放入和取出数据的速率问题. 从字面就可以理解, ...

  7. Golang结构体struct的使用(结构体嵌套, 匿名结构体等)

    转自: https://studygolang.com/articles/11313 golang中是没有class的,但是有一个结构体struct,有点类似,他没有像java,c++中继承的概念,但 ...

  8. Golang的sync.WaitGroup 实现逻辑和源码解析

    在Golang中,WaitGroup主要用来做go Routine的等待,当启动多个go程序,通过waitgroup可以等待所有go程序结束后再执行后面的代码逻辑,比如: func Main() { ...

  9. golang 的 sync.WaitGroup

    WaitGroup的用途:它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成. 官方对它的说明如下: A WaitGroup waits for ...

  10. golang的sync.WaitGroup使用示例

    下面一段代码 len(m) 不一定会打印为 10,为什么?.如果想要 len(m) 打印为 10,应该怎么修改代码? func main() { const N = 10 m := make(map[ ...

随机推荐

  1. js过滤掉指定html标签

    替换标签 var str = "<p><span style='color:#ccc;'>这是测试标签</span><span>这是测试htm ...

  2. layui踩坑记

    1)layui 中选择项里面的文字内容比较多,输入框的宽度不够,需求方要求扩大宽度到现在的2倍. 从网上搜索到的方法都是通过修改上一层的DIV的宽度来实现,修改之后大概的代码大概是这样的 <di ...

  3. prometheus Alertmanager webhook

      一.自定义邮件告警 二.使用docker部署微信机器人告警 1.制作镜像 2.启动容器和指定webhook容器 一.自定义邮件告警 在alertmanager服务的配置文件中指定自定义告警文件 # ...

  4. 写于vue3.0发布前夕的helloworld

    前言: vue3.0马上要来了,于今昔写一篇vue将一个字符串hellowrold渲染于页面的过程,慰藉我这几个月写vue的'枯燥'. 源码版本是2.6.10. 开始: 我们的模板足够简单: < ...

  5. 记录坑:Chrome谷歌浏览器最小化和页面遮挡后JS代码不稳定

    问题:用定时器 setInterval()做个滚动通知的动画,浏览器最小化时,定时器 setInterval()失效了,导致滚动条重叠了 可能原因: js代码不稳定 Chrome谷歌浏览器最小化和页面 ...

  6. CF1528D It's a bird! No, it's a plane! No, it's AaParsa!

    个人思路: floyd 求最短路,\(\Theta(n^3)\) 不能维护边的变化. 然后就不会做了. 正解: 首先,对于每个起始点,到达一个点 \(v\) 越早越好,因为可以等待. 边的变化相当于每 ...

  7. 1238. 循环码排列 (Medium)

    问题描述 1238. 循环码排列 (Medium) 给你两个整数 n 和 start.你的任务是返回任意 (0,1,2,,...,2^n-1) 的排列 p,并且满足: p[0] = start p[i ...

  8. npm设置和取消代理的方法

    设置代理 npm config set proxy=http://server:port npm config set https-proxy https://server:port // https ...

  9. etcd 基于ubuntu 20.04 部署集群

    Etcd是Kubernetes集群中的一个十分重要的组件,用于保存集群所有的网络配置和对象的状态信息,K8S中所有持久化的状态信息都是以Key-Value的形式存储在etcd中,提供分布式协调服务.之 ...

  10. FFmpeg 命令行

    FFmpeg命令行帮助 #>ffmpeg -h #>ffmpeg -h long #>ffmpeg -h full 将视频按照指定的宽高输出 #>ffmpeg -i input ...