mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
111 lines
2.4 KiB
Markdown
111 lines
2.4 KiB
Markdown
# 同步原语模式
|
|
|
|
互斥锁和原子操作的详细模式 —— 涵盖互斥锁嵌入陷阱和类型安全的原子访问。
|
|
|
|
---
|
|
|
|
## 不要嵌入互斥锁
|
|
|
|
如果你通过指针使用结构体,互斥锁应该是非指针字段。不要在结构体中嵌入互斥锁,即使该结构体未被导出。
|
|
|
|
```go
|
|
// 不好:嵌入的互斥锁将 Lock/Unlock 暴露为 API 的一部分
|
|
type SMap struct {
|
|
sync.Mutex // Lock() 和 Unlock() 成为 SMap 的方法
|
|
data map[string]string
|
|
}
|
|
|
|
func (m *SMap) Get(k string) string {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
return m.data[k]
|
|
}
|
|
```
|
|
|
|
```go
|
|
// 好:命名字段使互斥锁保持为实现细节
|
|
type SMap struct {
|
|
mu sync.Mutex
|
|
data map[string]string
|
|
}
|
|
|
|
func (m *SMap) Get(k string) string {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
return m.data[k]
|
|
}
|
|
```
|
|
|
|
在不好的示例中,`Lock` 和 `Unlock` 方法无意中成为了导出 API 的一部分。在好的示例中,互斥锁是对调用方隐藏的实现细节。
|
|
|
|
---
|
|
|
|
## 原子操作:完整示例
|
|
|
|
标准 `sync/atomic` 包操作原始类型(`int32`、`int64` 等),容易忘记一致地使用原子操作。
|
|
|
|
```go
|
|
// 不好:容易忘记原子操作
|
|
type foo struct {
|
|
running int32 // 原子操作
|
|
}
|
|
|
|
func (f *foo) start() {
|
|
if atomic.SwapInt32(&f.running, 1) == 1 {
|
|
return // 已在运行
|
|
}
|
|
// 启动 Foo
|
|
}
|
|
|
|
func (f *foo) isRunning() bool {
|
|
return f.running == 1 // 竞争!忘记使用 atomic.LoadInt32
|
|
}
|
|
```
|
|
|
|
```go
|
|
// 好:类型安全的原子操作
|
|
type foo struct {
|
|
running atomic.Bool
|
|
}
|
|
|
|
func (f *foo) start() {
|
|
if f.running.Swap(true) {
|
|
return // 已在运行
|
|
}
|
|
// 启动 Foo
|
|
}
|
|
|
|
func (f *foo) isRunning() bool {
|
|
return f.running.Load() // 不可能意外地非原子读取
|
|
}
|
|
```
|
|
|
|
`atomic.Bool`、`atomic.Int64` 等类型(Go 1.19 起在标准库 `sync/atomic` 中可用,或通过 [go.uber.org/atomic](https://pkg.go.dev/go.uber.org/atomic))通过隐藏底层类型来增加类型安全性。
|
|
|
|
---
|
|
|
|
## Channel 方向示例
|
|
|
|
指定方向可以防止意外误用:
|
|
|
|
```go
|
|
// 好:指定方向 - 清晰的所有权
|
|
func sum(values <-chan int) int {
|
|
total := 0
|
|
for v := range values {
|
|
total += v
|
|
}
|
|
return total
|
|
}
|
|
```
|
|
|
|
```go
|
|
// 不好:未指定方向 - 允许意外误用
|
|
func sum(values chan int) (out int) {
|
|
for v := range values {
|
|
out += v
|
|
}
|
|
close(values) // 漏洞!这能通过编译但不应该发生。
|
|
}
|
|
```
|