我正在尝试实现访问地图的模式,该模式允许多个读者和只有一个作者,并且密钥仅写入一次地图。
我想确保密钥(如果存在)只被放入地图一次。然后一旦添加了键,读者就会从地图中获取值。
它认为这是正确的,尽管主线程可以在所有 goroutine 完成之前退出,因此我并不总是看到 10 Exit 的打印。这是正确的实施模式吗?
package main
import (
"fmt"
"sync"
"time"
)
var downloads map[string]*sync.WaitGroup
var downloadsLock sync.RWMutex
func concurrentAccessTest() {
// Acquire the read lock to check if the items is already there.
downloadsLock.RLock()
if wg, ok := downloads["item"]; ok {
// Item exists
downloadsLock.RUnlock()
wg.Wait()
} else {
downloadsLock.RUnlock()
fmt.Println("Upgrade Lock")
// Item does not exist we need to add it to the map
downloadsLock.Lock()
fmt.Println("Writer lock obtained")
// Check another thread hasn't gone down this path and added the item to the map
if wg, ok := downloads["item"]; ok {
// Item exists, no need to add it, unlock and wait
downloadsLock.Unlock()
wg.Wait()
fmt.Println("Ok, now we can proceed")
} else {
// We are first in. Add and unlock
wg = &sync.WaitGroup{}
downloads["item"] = wg
downloadsLock.Unlock()
wg.Add(1)
fmt.Println("Do some time consuming stuff!")
time.Sleep(5 * time.Second)
wg.Done()
}
}
fmt.Println("Exit")
}
func main() {
downloads = make(map[string]*sync.WaitGroup)
// Add to the map
for i := 0; i < 10; i++ {
go concurrentAccessTest()
}
concurrentAccessTest()
// Wait for all threads to exit
fmt.Println("Done!")
}
Run Code Online (Sandbox Code Playgroud)
正如您的代码所示,无法从读取锁定升级为写入锁定,您必须释放读取锁定,然后获取写入锁定。
我认为你已经很接近了,它本身的使用看起来很好,但是我相信你在释放锁后RWMutex调用时存在潜在的问题。wg.Add(1)其他 goroutine 之一有可能会从映射中读取 WaitGroup 并在执行对 Add() 的调用之前调用 Wait(),根据 WaitGroup 文档,这是一个问题。文档说Note that calls with a positive delta that occur when the counter is zero must happen before a Wait.您可以通过将wg.Add(1)调用移到 Unlock() 之前轻松解决此问题
| 归档时间: |
|
| 查看次数: |
2351 次 |
| 最近记录: |