我有一个场景,我需要超出(尽可能多)地图条目并将它们发送到一个频道。通道另一端的操作可能需要很长时间,并且同时访问地图(并由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)
}
}
答案 0 :(得分:1)
我看到3个选择:
0)快照不一致这就是您所拥有的:当您生成密钥时,您的地图会发生变化,因此您可以获得所需的内容。我不完全确定你的锁定是否正确。它看起来真的很可疑。当然,用比赛探测器进行广泛的测试。
1)“停止世界” - 您可以在生成所有密钥时阻止对地图的写入权限。生成密钥比处理项目快得多,并且将为您提供要处理的项目的完美一致快照。不幸的是,当您将密钥发送到协同例程时,这些密钥在您处理它时可能不存在。这听起来你很好。它确实需要存储所有密钥的副本,所以希望这没关系。
2)滚动你自己的MVCC (多版本并发控制) - 不要使用1个地图,而是使用2.我们称它们为A和B.我们的想法是只写入第一个地图当你处理第二张地图时,然后翻转角色。
当你开始你的后台工作时,只需取出RW锁定,同时翻转布尔值。现在,您可以遍历“后退”地图的键,为每个键调用一个gouroutine。密钥保证存在,因为没有人写入该地图。
您可以从goroutine中的B中删除(在阅读时需要使用锁定,在删除时需要再次使用)。
但是处理所有没有锁定的条目(因为一切都只是阅读)可能会更好/更简单,然后等待所有 goroutines完成,然后通过执行来消除B “B = make()”得到一张空地图。这将立即释放所有内存,并保存一些在删除后需要完成的记帐。
擦除地图(或删除了所有条目)后,您可以在另一种方式翻转布尔值时使用RW锁定,然后开始处理其他地图。
缺点是,如果您经常更新项目,最终将获得2份地图副本。如果是这种情况:1)有WRITES检查后备地图。如果它在那里,请更新它,否则更新主地图。 2)在后台处理之前从地图中删除项目。 (你不能使用批量删除技巧。)