golang sync.WaitGroup错误使用导致死锁以及noCopy结构体介绍
背景
项目中遇到死锁,使用搜索引擎搜索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
golang sync.WaitGroup错误使用导致死锁以及noCopy结构体介绍的更多相关文章
- Golang Sync.WaitGroup 使用及原理
Golang Sync.WaitGroup 使用及原理 使用 func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.A ...
- Golang sync.WaitGroup的用法
0x01 介绍 经常会看到以下了代码: 12345678910111213 package main import ( "fmt" "time") func m ...
- golang sync.WaitGroup
//阻塞,直到WaitGroup中的所以过程完成. import ( "fmt" "sync" ) func wgProcess(wg *sync.WaitGr ...
- golang(07)结构体介绍
golang支持面向对象的设计,一般支持面向对象的语言都会有class的设计,但是golang没有class关键字,只有struct结构体.通过结构体达到类的效果,这叫做大成若缺,其用不弊. stru ...
- golang sync.WaitGroup bug
注意,这个结构体,要是想在函数之间传来传去的话,必须要使用指针....... 这个结构体里没有 指针,这个类型可以说没有“引用特性”. 被坑了一晚上.特此记录.
- golang-----golang sync.WaitGroup解决goroutine同步
go提供了sync包和channel来解决协程同步和通讯.新手对channel通道操作起来更容易产生死锁,如果时缓冲的channel还要考虑channel放入和取出数据的速率问题. 从字面就可以理解, ...
- Golang结构体struct的使用(结构体嵌套, 匿名结构体等)
转自: https://studygolang.com/articles/11313 golang中是没有class的,但是有一个结构体struct,有点类似,他没有像java,c++中继承的概念,但 ...
- Golang的sync.WaitGroup 实现逻辑和源码解析
在Golang中,WaitGroup主要用来做go Routine的等待,当启动多个go程序,通过waitgroup可以等待所有go程序结束后再执行后面的代码逻辑,比如: func Main() { ...
- golang 的 sync.WaitGroup
WaitGroup的用途:它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成. 官方对它的说明如下: A WaitGroup waits for ...
- golang的sync.WaitGroup使用示例
下面一段代码 len(m) 不一定会打印为 10,为什么?.如果想要 len(m) 打印为 10,应该怎么修改代码? func main() { const N = 10 m := make(map[ ...
随机推荐
- js右键生成菜单
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Flink Application Development DataStream API Event Time--Flink应用开发DataStream API事件时间
目录 概览 事件时间 接下来去哪儿 水印生成 水印策略简介 使用水印策略 处理空闲源 写水印生成代码 写周期WatermarkGenerator代码 写符号形式的WatermarkGenerator代 ...
- VUE3声明插件TypeScript类型
declare module '*.vue' { import type { DefineComponent } from 'vue'; // eslint-disable-next-line @ty ...
- C# snaps
C# 启用 禁用 本地连接 1 static void Enable(string interfaceName) 2 { 3 System.Diagnostics.ProcessStartInfo p ...
- b站
题目描述 n条鱼,每条鱼的体积是ai 每一轮,每一条鱼一定会吃掉右边比自己小的第一条鱼,一条鱼只能被吃一次. 多少轮后,鱼的数量会稳定. 例子: 6 6 3 3 --> 6 6 3(第二个3)- ...
- 原创:USB HID读卡器数据解析(R321-13.56MHZ读卡器)
1.工具准备 USB 监视软件:Device Monitoring Studio7.25 PC端软件:单片机多功能调试助手 2.发送数据包 接收数据包 3.数据分析 usb hid(pc软件)发送帧( ...
- httpcanary高级版--不闪退!!!!
地址 https://wwm.lanzouw.com/iOf7Hz11s4j 密码:45of
- Kubernetes基础配置管理
一.ConfigMap创建 1-1.基于目录创建ConfigMap 首先创建一个configmap/conf存储目录,分别建立.conf文件 mkdir /configmap/conf #创建.con ...
- Lecture 2. Fundamental Concepts and ISA - Carnegie Mellon - Computer Architecture 2015 - Onur Mutlu
并不只有冯诺依曼模型,按照控制流顺序执行指令 还有 data flow 模型,按照数据流顺序执行指令 冯诺依曼模型和数据流模型的编程语言的一个对比 Control-driven 编程模型和 data- ...
- python对象的三要素
id() 函数返回对象的唯一标识符,标识符是一个整数. 返回值 :返回对象的内存地址. >>>a = 'runoob' >>> id(a) 4531887632 i ...