Go并发编程——Waitgroup与mutex的问题

Waitgroup用于并发控制,mutex即互斥锁用于资源控制

Mutex

互斥锁 sync.mutex ,在引用时不加指针则为传值。

mutex实例无需实例化,声明即可使用 mutex在传递给外部使用的时候,需要传指针,不然传的是拷贝,会引起锁失败。并且指针的mutex是一定要实例化过

如以下A程序不会死锁,B程序会死锁(因为是同一个锁,没有unlock就再次lock)

A

package main

import (
	"fmt"
	"sync"
)

func sumMutexLock1(s sync.Mutex)  {
	s.Lock()
	fmt.Printf("sumMutexLock1, s: %p\n", &s)
	//defer s.Unlock()
}

func sumMutexLock2(s sync.Mutex)  {
	s.Lock()
	fmt.Printf("sumMutexLock2, s: %p\n", &s)
	defer s.Unlock()
}

func main() {
	mutex := sync.Mutex{}
	fmt.Printf("TestMutex21, s: %p\n", &mutex)
	sumMutexLock1(mutex)
	sumMutexLock2(mutex)
	fmt.Println("TestMutex1")
}

B

package main

import (
"fmt"
"sync"
)

func sumMutexLock1(s *sync.Mutex)  {
s.Lock()
fmt.Printf("sumMutexLock1, s: %p\n", &s)
//defer s.Unlock()  此处没有释放
}

func sumMutexLock2(s *sync.Mutex)  {
s.Lock()
fmt.Printf("sumMutexLock2, s: %p\n", &s)
defer s.Unlock()
}

func main() {
mutex := sync.Mutex{}
fmt.Printf("TestMutex21, s: %p\n", &mutex)
sumMutexLock1(&mutex)
sumMutexLock2(&mutex)
fmt.Println("TestMutex1")
}

WaitGroup

  • WaitGroup 传递给goroutine的时候,应该采用引用(指针)方式,从而避免发生副本拷贝而死锁
package main

import (
    "fmt"
    "sync"
)
var num = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
    //互斥锁
    m.Lock()     //当有线程进去进行加锁
    num = num + 1
    m.Unlock()   //出来后解锁,其他线程才可以进去
    wg.Done()
}
var w sync.WaitGroup
    var m sync.Mutex
    for i := 0; i < 1000; i++ {
        w.Add(1)
        go increment(&w, &m)//这里要传引用并不能传值,如果传值,那么每个协程都会得到 Mutex 的一份拷贝,竞态条件还是会发生。
    }
    w.Wait()
    fmt.Println("num =", num)

工具go vet

Go vet 命令在编写代码时非常有用。它可以帮助您检测应用程序中任何可疑、异常或无用的代码。该命令实际上由几个子分析器组成,甚至可以与您的自定义分析器一起工作。

例子:(不传指针)

func add(count *int, wg sync.WaitGroup, lock sync.Mutex)
.......

go add(&count, wg, lock)
.\测试互斥锁的传值.go:7:25: add passes lock by value: sync.WaitGroup contains sync.noCopy
.\测试互斥锁的传值.go:7:46: add passes lock by value: sync.Mutex
.\测试互斥锁的传值.go:22:16: call of add copies lock value: sync.WaitGroup contains sync.noCopy
.\测试互斥锁的传值.go:22:20: call of add copies lock value: sync.Mutex

区分Locker(接口)与Mutex(结构体)

type Locker

type Locker interface {
    Lock()
    Unlock()
}

Locker接口代表一个可以加锁和解锁的对象。

type Mutex

type Mutex struct {
    // 包含隐藏或非导出字段
}

Mutex是一个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。

func (*Mutex) Lock

func (m *Mutex) Lock()

Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。

func (*Mutex) Unlock

func (m *Mutex) Unlock()

Unlock方法解锁m,如果m未加锁会导致运行时错误。锁和线程无关,可以由不同的线程加锁和解锁。

 

实现并发控制,用  github.com/remeh/sizedwaitgroup包

package main

import (
	"fmt"
	"time"

	"github.com/remeh/sizedwaitgroup"
)

func main() {

	swg := sizedwaitgroup.New(5)
	for i := 0; i < 20; i++ {
		swg.Add()
		go printNum(i,&swg)
	}
	swg.Wait()
}

func printNum(i int,swg *sizedwaitgroup.SizedWaitGroup) {
	defer swg.Done()
	fmt.Println(i)
	time.Sleep(time.Second)
}

 

 

总结:

  1. 声明的时候都不用指针的形式,直接声明即可
  2. 在外部引用时都需要传指针,避免浅拷贝问题
  3. 3 需要实例化才能传指针
  4. sync包下的类型的值都不应被拷贝

 

参考链接:

1 官方文档

2 sizedwaitgroup

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值