# 首先写测试

``````func TestCounter(t *testing.T) {
t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) {
counter := Counter{}
counter.Inc()
counter.Inc()
counter.Inc()

if counter.Value() != 3 {
t.Errorf("got %d, want %d", counter.Value(), 3)
}
})
}``````

# 尝试运行测试

``./sync_test.go:9:14: undefined: Counter``

# 为要运行的测试编写最少的代码，并检查失败的测试输出

``````type Counter struct {

}``````

``````./sync_test.go:14:10: counter.Inc undefined (type Counter has no field or method Inc)
./sync_test.go:18:13: counter.Value undefined (type Counter has no field or method Value)``````

``````func (c *Counter) Inc() {

}

func (c *Counter) Value() int {
return 0
}``````

``````=== RUN   TestCounter
=== RUN   TestCounter/incrementing_the_counter_3_times_leaves_it_at_3
--- FAIL: TestCounter (0.00s)
--- FAIL: TestCounter/incrementing_the_counter_3_times_leaves_it_at_3 (0.00s)
sync_test.go:27: got 0, want 3``````

# 写足够的代码让测试通过

``````type Counter struct {
value int
}

func (c *Counter) Inc() {
c.value++
}

func (c *Counter) Value() int {
return c.value
}``````

# 重构

``````t.Run("incrementing the counter 3 times leaves it at 3", func(t *testing.T) {
counter := Counter{}
counter.Inc()
counter.Inc()
counter.Inc()

assertCounter(t, counter, 3)
})

func assertCounter(t *testing.T, got Counter, want int)  {
t.Helper()
if got.Value() != want {
t.Errorf("got %d, want %d", got.Value(), want)
}
}``````

# 首先写测试

``````t.Run("it runs safely concurrently", func(t *testing.T) {
wantedCount := 1000
counter := Counter{}

var wg sync.WaitGroup

for i:=0; i<wantedCount; i++ {
go func(w *sync.WaitGroup) {
counter.Inc()
w.Done()
}(&wg)
}
wg.Wait()

assertCounter(t, counter, wantedCount)
})``````

# 尝试运行测试

``````=== RUN   TestCounter/it_runs_safely_in_a_concurrent_envionment
--- FAIL: TestCounter (0.00s)
--- FAIL: TestCounter/it_runs_safely_in_a_concurrent_envionment (0.00s)
sync_test.go:26: got 939, want 1000
FAIL``````

# 写足够的代码让测试通过

Mutex 是互斥锁。互斥锁的零值是一个解锁的互斥锁。

``````type Counter struct {
mu sync.Mutex
value int
}

func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}``````

# 我还看到过其他的例子。互斥锁被嵌入到 struct 中

``````type Counter struct {
sync.Mutex
value int
}``````

``````func (c *Counter) Inc() {
c.Lock()
defer c.Unlock()
c.value++
}``````

# 复制互斥锁（mutex）

``````sync/v2/sync_test.go:16: call of assertCounter copies lock value: v1.Counter contains sync.Mutex
sync/v2/sync_test.go:39: assertCounter passes lock by value: v1.Counter contains sync.Mutex``````

``func assertCounter(t *testing.T, got *Counter, want int)``

``````func NewCounter() *Counter {
return &Counter{}
}``````

# 结束

• `Mutex` 允许我们在数据中添加锁

• `Waitgroup` 是等待 goroutines 完成工作的一种方式

## 何时在通道和 goroutines 上使用锁?

• 在传递数据所有权时使用通道

• 使用互斥锁来管理状态

## 不要使用嵌入，因为它很方便

• 考虑一下嵌入对公共 API 的影响。

• 真的 想要公开这些方法并让人们将自己的代码耦合到里面吗?

• 对于互斥锁，这可能会以非常不可预测和奇怪的方式带来潜在的灾难性后果，想象一下，一些邪恶的代码在不应该解锁互斥锁的时候解锁了它;这将导致一些非常奇怪的 bug，很难跟踪。