前言
sync.mutex
为什么要使用互斥锁
10000num
num++num加1num10goroutinenum++num=1000加1num=1001
package main
import (
"fmt"
"sync"
)
func main() {
num := 0
var wg sync.waitgroup
threadcount := 10000
wg.add(threadcount)
for i := 0; i < threadcount; i++ {
go func() {
defer wg.done()
num++
}()
}
wg.wait() // 等待 10000 个协程都执行完
fmt.println(num) // 9388(每次都可能不一样)
}
goroutinegoroutinegoroutine
并发获取锁示意图
package main
import (
"fmt"
"sync"
)
func main() {
num := 0
var mutex sync.mutex // 互斥锁
var wg sync.waitgroup
threadcount := 10000
wg.add(threadcount)
for i := 0; i < threadcount; i++ {
go func() {
defer wg.done()
mutex.lock() // 加锁
num++ // 临界区
mutex.unlock() // 解锁
}()
}
wg.wait()
fmt.println(num) // 10000
}
如何使用互斥锁
mutexgolock()unlock()
使用方式一:直接声明使用
这个在上例中已经体现了,直接看上面的例子就好
使用方式二:封装在其他结构体中
mutexstructadd()count()
package main
import (
"fmt"
"sync"
)
type counter struct {
num int
mutex sync.mutex
}
// 加一操作,涉及到临界区 num,加锁解锁
func (counter *counter) add() {
counter.mutex.lock()
defer counter.mutex.unlock()
counter.num++
}
// 返回数量,涉及到临界区 num,加锁解锁
func (counter *counter) count() int {
counter.mutex.lock()
defer counter.mutex.unlock()
return counter.num
}
func main() {
threadcount := 10000
var counter counter
var wg sync.waitgroup
wg.add(threadcount)
for i := 0; i < threadcount; i++ {
go func() {
defer wg.done()
counter.add()
}()
}
wg.wait() // 等待所有 goroutine 都执行完
fmt.println(counter.count()) // 10000
}
gomappanic
// 运行会 panic,提示 fatal error: concurrent map writes
func main() {
m := make(map[string]string)
var wait sync.waitgroup
wait.add(1000)
for i := 0; i < 1000; i++ {
item := fmt.sprintf("%d", i)
go func() {
wait.done()
m[item] = item
}()
}
wait.wait()
}
mutexmap
import (
"fmt"
"sync"
)
type concurrentmap struct {
mutex sync.mutex
items map[string]interface{}
}
func (c *concurrentmap) add(key string, value interface{}) {
c.mutex.lock()
defer c.mutex.unlock()
c.items[key] = value
}
func (c *concurrentmap) remove(key string) {
c.mutex.lock()
defer c.mutex.unlock()
delete(c.items, key)
}
func (c *concurrentmap) get(key string) interface{} {
c.mutex.lock()
defer c.mutex.unlock()
return c.items[key]
}
func newconcurrentmap() concurrentmap {
return concurrentmap{
items: make(map[string]interface{}),
}
}
func main() {
m := newconcurrentmap()
var wait sync.waitgroup
wait.add(1000)
for i := 0; i < 1000; i++ {
item := fmt.sprintf("%d", i)
go func() {
wait.done()
m.add(item, item)
}()
}
wait.wait()
fmt.println(m.get("100")) // 100
}
mutexmapsync.rwmutexmapmap
互斥锁的常见问题
mutex
mutexgoroutine agoroutine b
mutexdeferdefer mutex.unlock()defermutex.unlock()
// 逻辑复杂,可能会忘记释放锁
func main() {
var mutex sync.mutex
mutex.lock()
if *** {
if *** {
// 处理临界区资源
mutex.unlock()
return
}
// 处理临界区资源
mutex.unlock()
return
}
// 处理临界区资源
mutex.unlock()
return
}
// 避免逻辑复杂忘记释放锁,使用 defer语句,成对出现
func main() {
var mutex sync.mutex
mutex.lock()
defer mutex.unlock()
if *** {
if *** {
// 处理临界区资源
return
}
// 处理临界区资源
return
}
// 处理临界区资源
return
}
3.mutex 不能复制使用
mutexmutexmutex
package main
import (
"fmt"
"sync"
)
type counter struct {
mutex sync.mutex
num int
}
func somefunc(c counter) {
c.mutex.lock()
defer c.mutex.unlock()
c.num--
}
func main() {
var counter counter
counter.mutex.lock()
defer counter.mutex.unlock()
counter.num++
// go都是值传递,这里复制了 counter,此时 counter.mutex 是加锁状态,在 somefunc 无法再次加锁,就会一直等待
somefunc(counter)
}