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)
}
总结:
- 声明的时候都不用指针的形式,直接声明即可
- 在外部引用时都需要传指针,避免浅拷贝问题
- 3 需要实例化才能传指针
- sync包下的类型的值都不应被拷贝
参考链接:
1 官方文档