-
Notifications
You must be signed in to change notification settings - Fork 0
/
第 09.03 章、sync 包
223 lines (138 loc) · 6.92 KB
/
第 09.03 章、sync 包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
sync 包提供底层的同步机制,比如互斥锁
除了 Once 和 WaitGroup 两种类型,大部分都被用于底层
高阶同步机制,应该通过 channel 和 communication 来完成
Cond struct 类型
Cond 实现了一个条件变量,一个让 gorountine 等待 或 声明 事件发生的 聚集地
每个 Cond 都有一个与之关联的 Locker L(一般是 *Mutex 或 *RWMutex)
当修改条件或者调用 Wait 方法时,Locker L 必须被持有(held)
Cond 可以作为其他结构的部分而被创建
在经过首次使用之后,Cond 就不可以被拷贝
type Cond struct{
// 当状态被观察或修改时, L 必须被持有
L Locker
// 其他未导出量
}
NewCond(l Locker) *Cond
产生一个内含 Locker l 的 Cond
(c *Cond) Broadcast()
Broadcast 唤醒所有正在等待 c 的 goroutine
它允许但不要求调用者持有 c.L
(c *Cond) Signal()
如果存在的话,Signal 唤醒一个正在等待 c 的 goroutine
它允许但不要求调用者持有 c.L
(c *Cond) Wait()
Wait 原子操作:解锁 c.L,并挂起调用它的 goroutine
在其后恢复执行后, Wait 会在返回前锁住 c.L
和其他系统不同, Wait 不会返回,除非被 Broadcast 和 Signal 唤醒
由于在 Wait 第一次恢复时 c.L 并未被锁,调用者通常不能假设在 Wait 返回时 条件为真
相反的,调用者需要在一个循环中 Wait
c.L.Lock()
for !condition(){
c.Wait()
}
... make use of condition ...
c.L.Unlock()
Locker interface 类型
Locker 表示一个物体可以被 Lock 和 Unlock
type Locker interface{
Lock()
Unlock()
}
type Mutex struct 类型
Mutex 既是互斥锁。
Mutexes 可以作为其他结构的部分被创建
Mutex 的零值为 Unlock 状态的 mutex
Mutex 在初次使用后便不该被拷贝
(m *Mutex) Lock()
锁住 m,如果锁正在被使用,则调用方法的 goroutine 一直阻塞,直到 mutex 可用
(m *Mutex) Unlock()
解锁 m,若 m 没有被锁住,则产生 run-time error
一个锁住的 Mutex 不和任何特定的 goroutine 关联,
它允许一个 goroutine 锁住 Mutex,然后让另一个 goroutine 解锁它
Once struct 类型
执行且仅执行一次行动的类型
(o *Once) Do(f func())
对于同一个 Once 实例, 传入 Do 方法的函数只会被执行一次
无论这个实例的 Do 方法被调用多少次,或在多少个不同的 goroutine 中被调用
在 f 返回前, Do 不会返回,所以,不要在 f 中再次调用 Do,这会让 goroutine 陷入 deadlock
即便 f 发出了 panic, Do 也会认为 f 返回了,其后任何操作都不会让这个 Once 实例再次执行 f
Pool strcut 类型
一个 Pool 就是一系列可以被独立保存和恢复的临时对象的集合
任何保存在 Pool 中的对象都可能在任何时候不加提示的被自动移除
若 Pool 具有的是某内存数据唯一的引用,则发生移除的时候,这部分数据可能会直接丢失
Pool 是并行安全的
Pool 用于缓存一些分配过的但还未使用的内存地址,以便其后再使用
这样可以减轻 GC 的压力
这让建立高效的、线程安全的 free list 变得简单
但它并不适合所有的 free list
一个正确的 Pool 的用法是:管理一组临时项目
这些零时项目将静默地在并发且独立的客户端中分享
并有可能再次被利用
Pool 提供了一种在多个客户端之间分配内存的方法
fmt 包就很好的利用了 Pool
它维护了一个动态大小的临时输出缓存
缓存空间在大量打印时会增长,在少有打印时会缩减
另一方面说,一个作为短暂存在的物体的一部分的 free list 并不适合用 Pool
因为 overhead 并不适合这种场景
让这些物体实现自己的 free list 比较高效
Pool 在首次使用之后,就不应被拷贝
type Pool strcut{
// 可选的 New 函数规定了一个函数来生成一个值,以供 Get 方法返回
// 一般情况下, Get 会返回 nil
New func() interface{}
// 其他隐藏的条目
}
(p *Pool) Get() interface{}
Get 会从 Pool 中任意选择一个项目,并把它从 Pool 中移除,并将这个项目返回给调用者
Get 可能会无视 pool,并认为它的空的
调用者不可认为 Get 和之前 Put 的值会有关联
若 Get 只能返回 nil,而 p.New 函数有返回值,则用 p.New() 的返回值替代 nil
(p *Pool) Put(x interface{})
向 pool 中添加 x
RWMutex strcut 类型
RWMutex 是 reader/writer 互斥锁
这把锁可以被任意多个 reader 或者 一个 writer 持有
RWMutex 可作为其他结构的组成部分而被创建
RWMutex 的零值是一个 解锁状态的互斥锁
RWMutex 在第一次使用后,便不应该被拷贝
如果一个 goroutine 为了读取数据持有一个 RWMutex,则它不能希望任何其他的调用者能同持有 RWMutex 的 read 锁
直到上一个 read 锁被释放之后,新的调用者才能持有 read 锁
具体来说这防止了循环 read 锁,保证了锁最终是可用的
一个已阻塞的 Lock 调用,将让新的 reader 无法获得 lock
(rw *RWMutex) Lock()
持有 rw 的写入锁
若 rw 已经被 read 锁住或者 write 锁住
Lock 函数会阻塞,直到锁再次可用
(rw *RWMutex) RLock()
持有 rw 的读取锁
(rw *RWMutex) RLocker() Locker
返回一个实现了 Locker interface 的实例
这个实例通过调用 rw.RLock 和 rw.RUnlock 来满足 Locker interface 的 Lock() 和 Unlock()
(rw *RWMutex) RUnlock()
RUnlock 解锁单个 RLock 调用,它不影响其他并行的 reader
如果 rw 并未被锁,则产生 run-time error
(rw *RWMutex) Unlock()
解锁 RWMutex 来写入
若 rw 并未被锁,则产生 run-time error
RWMutex 和 Mutex 一样,不和特定的 goroutine 绑定
一个 goroutine 锁定的 RWMutex 可以由其他的 goroutine 解锁
WaitGroup struct 类型
一个 WaitGroup 实例会等待一组 goroutine 结束
主 goroutine 调用 Add 来设置需要等待的 goroutine 的数量
接着每个 goroutine 运行并在返回时调用 Done
于此同时, Wait 可以用于阻塞,直到所有的 goroutine 全部结束
WaitGroup 在使用之后便不该被拷贝
(wg *WaitGroup) Add(delta int)
将一个加数加入 WaitGroup 的计数器中,这个加数可以是负数
若计数器变为 0,则所有被 Wait 阻塞的 goroutine 全部被释放
若计数器变为负数,则 Add 方法 panic
注意,若要在 计数器 为 0 的时候,用 Add 方法为计数器加一个正数,
则这个 Add 语句必须出现在 Wait 语句之前
当 计数器 大于 0 的时候,带负数或正数的调用可以出现在任何时候
代表性地来说,这意味着,Add 的执行应早于创建 goroutine 和其他需要等待的事件的语句前
如果一个 WaitGroup 被用在等待不同组相互独立的事件组之中,
则新的 Add 调用必须发生在全部前序的 Wait 调用全部返回之时才可以
(wg *WaitGroup) Done()
Done 方法减少 WaitGroup 的计数器
(wg *WaitGroup) Wait()
Wait 方法保持阻塞,直到 WaitGroup 的计数器到 0