• 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

    sync.WaitGroup的使用以及坑

  • 相关阅读:
    平方和公式
    $bootpuss$切不掉的「水题」
    回滚莫队初步
    [***]HZOJ 柱状图
    HZOJ 走格子
    HZOJ 旋转子段
    [***]HZOJ 优美序列
    [***]HZOJ 跳房子
    HZOJ 矩阵游戏
    模板—K-D-tree(P2479 [SDOI2010]捉迷藏)
  • 原文地址:https://www.cnblogs.com/luoming1224/p/14667386.html
Copyright © 2020-2023  润新知