# sync包详解

Golang的`sync`包提供了常见的并发编程同步原语，所谓原语是指不可被中断的，具有原子性的程序段。

## sync.Mutex

mutex是互斥的意思，`sync.Mutex`代表互斥锁，代表在共享资源上互斥访问（不可同时访问）。

```go
mutex := &sync.Mutex{}

mutex.Lock()
// Update共享变量 (比如切片，结构体指针等)
mutex.Unlock()
```

## sync.RWMutex

读写锁实现，除了提供`Lock`和`UnLock`方法用做并发写入，还提供了`RLock`和`RUnlock`方法进行并发读取：

```go
mutex := &sync.RWMutex{}
mutex.Lock()
// Update 共享变量
mutex.Unlock()

mutex.RLock()
// Read 共享变量
mutex.RUnlock()
```

## sync.WaitGroup

`sync.WaitGroup`拥有一个内部计数器。当计数器等于`0`时，则`Wait()`方法会立即返回。否则它将阻塞执行`Wait()`方法的`goroutine`直到计数器等于`0`时为止。

要增加计数器，我们必须使用`Add(int)`方法。要减少它，我们可以使用`Done()`（将计数器减`1`），也可以传递负数给`Add`方法把计数器减少指定大小，`Done()`方法底层就是通过`Add(-1)`实现的。

```go
g := &sync.WaitGroup{}

for i := 0; i < 8; i++ {
  wg.Add(1)
  go func() {
    // Do something
    wg.Done()
  }()
}

wg.Wait()
// 继续往下执行...
```

## sync.Once

`sync.Once`是一个简单而强大的原语，可确保一个函数仅执行一次。在下面的示例中，只有一个`goroutine`会显示输出消息：

```go
once := &sync.Once{}
for i := 0; i < 4; i++ {
    i := i
    go func() {
        once.Do(func() {
            fmt.Printf("first %d\n", i)
        })
    }()
}
```

我们使用了`Do(func ())`方法来指定只能被调用一次的部分。

## sync.Map

`sync.Map`是一个并发版本的`Go`语言的`map`，我们可以：

* 使用`Store(interface {}，interface {})`添加元素。
* 使用`Load(interface {}) interface {}`检索元素。
* 使用`Delete(interface {})`删除元素。
* 使用`LoadOrStore(interface {}，interface {}) (interface {}，bool)`检索或添加之前不存在的元素。如果键之前在`map`中存在，则返回的布尔值为`true`。
* 使用`Range`遍历元素。

```go
m := &sync.Map{}

// 添加元素
m.Store(1, "one")
m.Store(2, "two")

// 获取元素1
value, contains := m.Load(1)
if contains {
  fmt.Printf("%s\n", value.(string))
}

// 返回已存value，否则把指定的键值存储到map中
value, loaded := m.LoadOrStore(3, "three")
if !loaded {
  fmt.Printf("%s\n", value.(string))
}

m.Delete(3)

// 迭代所有元素
m.Range(func(key, value interface{}) bool {
  fmt.Printf("%d: %s\n", key.(int), value.(string))
  return true
})
```
