前言

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)

}