【发布时间】:2017-02-19 14:50:41
【问题描述】:
我有一个场景,我需要覆盖(尽可能多的)地图条目并将它们发送到通道中。通道另一端的操作可能需要很长时间,并且地图是同时访问的(并受RWMutex 保护)。地图也相当大,我想避免创建它的临时副本。
假设我有一个这样的结构:
type Example struct {
sync.RWMutex
m map[string]struct{}
}
现在我想出了这样的东西:
func (e *Example) StreamAll() <-chan string {
toReturn := make(chan string)
go func() {
e.RLock()
defer e.RUnlock()
for k := range e.m {
e.RUnlock()
toReturn <- k
e.RLock()
}
close(toReturn)
}()
return toReturn
}
language specification 有一个关于地图测距的有趣之处:
如果在迭代过程中删除了尚未到达的映射条目,则不会产生相应的迭代值。如果在迭代过程中创建了映射条目,则该条目可能会在迭代过程中生成,也可能会被跳过。
现在,我想知道的是:即使地图在迭代之间发生变化,是否可以保证我在地图上测距的方法有效?包括我上次读取的密钥被删除的情况?我不需要所有地图条目,但大部分都需要。
这是一个完整的例子:
package main
import (
"fmt"
"sync"
)
type Example struct {
sync.RWMutex
m map[string]struct{}
}
func NewExample() *Example {
return &Example{
m: make(map[string]struct{}),
}
}
func (e *Example) Put(s string) {
e.Lock()
defer e.Unlock()
e.m[s] = struct{}{}
}
func (e *Example) Delete(s string) {
e.Lock()
defer e.Unlock()
delete(e.m, s)
}
func (e *Example) StreamAll() <-chan string {
toReturn := make(chan string)
go func() {
e.RLock()
defer e.RUnlock()
for k := range e.m {
e.RUnlock()
toReturn <- k
e.RLock()
}
close(toReturn)
}()
return toReturn
}
func main() {
e := NewExample()
e.Put("a")
e.Put("b")
values := e.StreamAll()
// Assume other goroutines concurrently call Put and Delete on e
for k := range values {
fmt.Println(k)
}
}
【问题讨论】:
-
您到操场的链接不起作用。
-
谢谢,我现在把它放到问题中。
-
range操作本身应该以并发安全的方式执行,如果您的地图正在更改,您可能会得到奇怪的元素序列(每次您在地图上进行范围时,你得到的元素顺序会有所不同。你会从地图中删除元素吗? -
是的,还有其他 goroutine 在
Example类型上调用Put和Delete。请注意,这两种方法都使用 Writer-Lock。我的逻辑是,只要 for-range 循环的range部分受到 Reader-Lock 的保护,我就不会并发读写。 -
我想知道这怎么会突然引起这么多关注。我记得在没有得到关于 SO 的答案后,我将它交叉发布到 golang-nuts,这是线程:groups.google.com/forum/#!topic/Golang-nuts/vkysJuKen1A
标签: go