-
Notifications
You must be signed in to change notification settings - Fork 0
/
10_channel高级和锁.go
339 lines (308 loc) · 7.44 KB
/
10_channel高级和锁.go
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*
* @Author: your name
* @Date: 2021-01-12 17:06:18
* @LastEditTime: 2021-01-16 21:43:12
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /demo/10_channel高级.go
*/
package main
import (
"fmt"
"image"
"image/jpeg"
"os"
"strconv"
"sync"
"sync/atomic"
"time"
)
var (
count int
rwcount int
awt sync.WaitGroup
lock sync.Mutex
rwlock sync.RWMutex
iconMap map[string]image.Image
mun int
syncOnce sync.Once
)
func caseOneChan(cn chan<- int) {
time.Sleep(time.Second) // 通过time.Sleep证实,不管等多久,select总会等到先回来的那个
cn <- 3
}
func caseTwoChan(cn chan<- int) {
time.Sleep(time.Second)
cn <- 6
}
// 有一个值可以从多个通道中获取,只要其中一个返回了结果就可以了,其他的就不再等
func multChanGetOneData() {
var cn1 = make(chan int)
var cn2 = make(chan int)
var data int
go caseOneChan(cn1)
go caseTwoChan(cn2)
select {
case data = <-cn1:
fmt.Printf("cn1先回来: %d\n", data)
case data = <-cn2:
fmt.Printf("cn2先回来: %d\n", data)
}
fmt.Println(data)
}
// 并发安全,多个goroutine操作同一个对象,这样会发生意想不到的结果
func nosafyGoroutine() {
defer awt.Done()
for i := 0; i < 100000; i++ {
count++
}
}
// 使用互斥锁来解决操作同一个对象的问题
func useLock() {
defer awt.Done()
for i := 0; i < 100000; i++ {
lock.Lock()
count++
lock.Unlock()
}
}
// 数字累加
func countNum() {
awt.Add(2)
go nosafyGoroutine()
go nosafyGoroutine()
awt.Wait()
// 结果并不是200000,因为同时操作了count这个变量
fmt.Printf("两次goroutine累加count的数字: %d\n", count)
count = 0
// 使用lock之后
awt.Add(2)
go useLock()
go useLock()
awt.Wait()
fmt.Printf("lock之后的计算的count: %d\n", count)
}
// 读锁和写锁
// 如果有多个goroutine同时对一个对象、资源读取,那么就没必要非要等一个goroutine读取之后另一个再读
// 如果有多个goroutine同时对一个对象、资源读写,那么:
// 1. 如果一个goroutine获取的是读锁,其他goroutine获取的不是读锁就可以继续进行,写锁原地等待
// 2. 如果一个goroutine获取的是写锁,其他goroutine无论是什么锁都要等待
// 使用读写锁(当读锁更多时,效果越明显)
func usewlock() {
defer awt.Done()
for i := 0; i < 100; i++ {
rwlock.Lock() // 加写锁
rwcount++
time.Sleep(time.Millisecond) // 假设读操作耗时10毫秒
rwlock.Unlock()
}
}
func userlock() {
defer awt.Done()
for i := 0; i < 1000; i++ {
rwlock.RLock() // 加读锁
// rwlock.Lock() // 依旧使用写锁
time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
fmt.Printf("%d\t", rwcount)
// rwlock.Unlock()
rwlock.RUnlock()
}
}
// 即有读又有写
func rwlockMain() {
start := time.Now()
awt.Add(3)
go userlock()
go usewlock()
go userlock()
awt.Wait()
end := time.Now()
between := end.Sub(start).Milliseconds()
fmt.Printf("相差毫秒数: %d\n", between) // 使用Rlock和RUnlock 1179秒,没有使用读锁2253秒
}
// 多个goroutine操作多次操作配置文件问题
func iconConf() {
iconMap = map[string]image.Image{
"left": loadIcon("./image/left.jpg"),
"right": loadIcon("./image/right.jpg"),
"top": loadIcon("./image/top.jpg"),
"bottom": loadIcon("./image/bottom.jpg"),
}
fmt.Println("iconConf执行了")
// 这个地方cpu可能解释为
/*
iconMap = make(map[string]image.Image)
iconMap["left"] = loadIcon("left.jpg")
iconMap["top"] = loadIcon("top.jpg")
iconMap["right"] = loadIcon("right.jpg")
iconMap["bottom"] = loadIcon("bottom.jpg")
**/
// 这样会导致可能iconMap已经做了make操作,但变量并未初始化
}
// 加载图片
func loadIcon(path string) image.Image {
file, err := os.Open(path)
if err != nil {
panic(err)
}
defer file.Close()
img, err := jpeg.Decode(file)
if err != nil {
panic(err)
}
return img
}
// 获取单个图片
func singleImg(img string, cn chan<- image.Image) {
defer awt.Done()
if iconMap == nil {
iconConf() // 配置文件只需要init一次就够了,但是这个例子中init了4次,而且是操作同一个文件,但是也不能吧这句代码放在外面,因为
}
lock.Lock()
mun++
lock.Unlock()
cn <- iconMap[img]
if mun >= 4 {
close(cn)
}
}
// 改良版获取单个图片
func useSyncOnce(img string, cn chan<- image.Image) {
defer awt.Done()
syncOnce.Do(iconConf) // 只初始化一次,当多次goroutine执行这句,只会等第一个执行完了,不再执行(多个goroutine只需一个执行,其他人等待)
cn <- iconMap[img]
lock.Lock()
mun++
lock.Unlock()
if mun >= 4 {
close(cn)
}
}
func getAllImg() {
awt.Add(4)
var cn = make(chan image.Image)
// go singleImg("left", cn)
// go singleImg("right", cn)
// go singleImg("top", cn)
// go singleImg("bottom", cn)
go useSyncOnce("left", cn)
go useSyncOnce("right", cn)
go useSyncOnce("top", cn)
go useSyncOnce("bottom", cn)
for img := range cn {
fmt.Printf("获取到的图片: %T\n", img)
}
awt.Wait()
}
var myMap map[string]int
var mapWait sync.WaitGroup
var mapLoc sync.Mutex
var syncMap = sync.Map{}
// 对字典进行赋值
func setMapVal(val int) {
defer mapWait.Done()
syncOnce.Do(func() {
myMap = make(map[string]int) // 多个goroutine只需要初始化一次
})
key := strconv.Itoa(val)
mapLoc.Lock() // 如果不加锁 fatal error: concurrent map writes
myMap[key] = val
mapLoc.Unlock()
}
// 设定一个字典
func initMyMap() {
var len = 20
for i := 0; i < len; i++ {
mapWait.Add(1) // waitGroup是可以累加的
// go setMapVal(i)
go useSyncMap(i)
}
mapWait.Wait()
syncMap.Range(func(key, val interface{}) bool {
fmt.Printf("val: %v\n", val)
return true
})
oneVal, _ := syncMap.Load("4")
twoVal, ok := syncMap.LoadOrStore("5", 6)
fmt.Printf("val: %d\n", oneVal)
if ok {
fmt.Printf("获取值twoVal: %d\n", twoVal)
} else {
fmt.Printf("更新值twoVal: %d\n", twoVal)
}
}
// 使用go内置的sync.Map来解决对map的保护
func useSyncMap(val int) {
defer mapWait.Done()
key := strconv.Itoa(val)
syncMap.Store(key, val)
}
var proWait sync.WaitGroup
// 如果是值类型,则可以使用原子来替代互斥锁和读写锁
type valueTypeOption interface {
read() int64
write(int64) int64
}
// lock的struct
type valueLock struct {
number int64
lock sync.RWMutex
}
// read方法
func (v *valueLock) read() int64 {
defer v.lock.RUnlock()
v.lock.RLock()
return v.number
}
// write方法
func (v *valueLock) write(n int64) int64 {
defer v.lock.RUnlock()
v.lock.RLock()
v.number = v.number + n
return v.number
}
// atomic的struct
type valueAtomic struct {
number int64
}
// atomic操作的读取
func (v *valueAtomic) read() int64 {
return atomic.LoadInt64(&v.number)
}
// atomic操作的设置
func (v *valueAtomic) write(n int64) int64 {
atomic.AddInt64(&v.number, n)
return v.number
}
// gorountine
func rwValue(st valueTypeOption, n int64) {
defer proWait.Done()
fmt.Printf("设置前num: %d\n", st.read())
fmt.Printf("设置后: %d\n", st.write(n))
}
// 对比lock锁和aotmic锁区别
func protectValue() {
var lov = valueLock{
number: 10,
}
var atv = valueAtomic{
number: 100,
}
for i := 1; i < 5; i++ {
proWait.Add(1)
go rwValue(&lov, int64(i))
}
for i := 1; i < 5; i++ {
proWait.Add(1)
go rwValue(&atv, int64(i))
}
proWait.Wait()
}
func initgrt() {
// multChanGetOneData()
// countNum()
// rwlockMain()
// getAllImg()
// initMyMap()
protectValue()
}