前言
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) }