douan4106 2018-05-04 00:22
浏览 54
已采纳

如何使用RWMutex和升级锁

I'm trying to implement the pattern of access to a map that allows multiple readers and only one writer and where the key is only written to the map once.

I want to make sure that the key, if it exists, is only ever placed into the map one time. And then once the key is added, the readers obtain the value from the map.

It think this is correct, although the main thread could exit before all the goroutines finish, hence I don't always see 10 Exit's printed. Is this the correct pattern of implementation?

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!")
}
  • 写回答

1条回答 默认 最新

  • duanqin7791 2018-05-04 00:30
    关注

    As your code shows, there's no way to upgrade from a read to a write lock, you have to release the read lock and then take the write lock.

    I think you're close, the use of RWMutex itself looks fine, however i believe you have a potential issue with calling wg.Add(1) after releasing the lock. There's a chance that one of the other goroutines will read the WaitGroup out of the map and call Wait() before the call to Add() is executed, which according to the WaitGroup docs is an issue. The docs say Note that calls with a positive delta that occur when the counter is zero must happen before a Wait. You can easily fix this by moving the wg.Add(1) call before the Unlock()

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 eda:门禁系统设计
  • ¥50 如何使用js去调用vscode-js-debugger的方法去调试网页
  • ¥15 376.1电表主站通信协议下发指令全被否认问题
  • ¥15 物体双站RCS和其组成阵列后的双站RCS关系验证
  • ¥15 复杂网络,变滞后传递熵,FDA
  • ¥20 csv格式数据集预处理及模型选择
  • ¥15 部分网页页面无法显示!
  • ¥15 怎样解决power bi 中设置管理聚合,详细信息表和详细信息列显示灰色,而不能选择相应的内容呢?
  • ¥15 QTOF MSE数据分析
  • ¥15 平板录音机录音问题解决