算法:实现阻塞读且并发安全的map
GO里面MAP如何实现key不存在 get操作等待 直到key存在或者超时,保证并发安全,且需要实现以下接口:
1
2
3
4
type sp interface {
Out(key string, val interface{}) //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回
Rd(key string, timeout time.Duration) interface{} //读取一个key,如果key不存在阻塞,等待key存在或者超时
}
解析:
看到阻塞协程第一个想到的就是channel
,题目中要求并发安全,那么必须用锁,还要实现多个goroutine
读的时候如果值不存在则阻塞,直到写入值,那么每个键值需要有一个阻塞goroutine
的 channel
。
实现如下:
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
type Map struct {
c map[string]*entry
rmx *sync.RWMutex
}
type entry struct {
ch chan struct{}
value interface{}
isExist bool
}
func (m *Map) Out(key string, val interface{}) {
m.rmx.Lock()
defer m.rmx.Unlock()
if e, ok := m.c[key]; ok {
e.value = val
e.isExist = true
close(e.ch)
} else {
e = &entry{ch: make(chan struct{}), isExist: true,value:val}
m.c[key] = e
close(e.ch)
}
}
func (m *Map) Rd(key string, timeout time.Duration) interface{} {
m.rmx.Lock()
if e, ok := m.c[key]; ok && e.isExist {
m.rmx.Unlock()
return e.value
} else if !ok {
e = &entry{ch: make(chan struct{}), isExist: false}
m.c[key] = e
m.rmx.Unlock()
fmt.Println("协程阻塞 -> ", key)
select {
case <-e.ch:
return e.value
case <-time.After(timeout):
fmt.Println("协程超时 -> ", key)
return nil
}
} else {
m.rmx.Unlock()
fmt.Println("协程阻塞 -> ", key)
select {
case <-e.ch:
return e.value
case <-time.After(timeout):
fmt.Println("协程超时 -> ", key)
return nil
}
}
}