doukong9316 2018-05-31 23:40
浏览 124
已采纳

Golang MGO模块是否锁定或解锁Go对象?

I am trying to understand whether Mongo locks Go objects.

The first function works fine with the json encoder, however the second function fails fatal error: sync: Unlock of unlocked RWMutex. Is this because mongo.Find is already trying to lock/unlock the state object? Do I need to externally handle race competition for my go objects or does MGO take care of it? I tried reading the source code but I haven't been able to reach a conclusion.

Any would be much appreciated!

import (
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"io"
"sync"
"encoding/json"
)

type ApplicationState struct {
    FileStates map[string]FileState     `json:"fileStates" bson:"fileStates"` 
    lock       sync.RWMutex             `json:"-" bson:"-"`
}

func (state *ApplicationState) ReadState(reader io.Reader) error {
    state.lock.Lock()
    defer state.lock.Unlock()
    return json.NewDecoder(reader).Decode(state)}

func (state *ApplicationState) ReadStateMGO(c *mgo.Collection) error {
    state.lock.Lock()
    defer state.lock.Unlock()
    return c.Find( bson.M{} ).Select( bson.M{"_id": 0} ).One(state)}

Note: to test it you can just replace the Filestate field with a string map.

  • 写回答

1条回答 默认 最新

  • dragon2025 2018-06-01 07:46
    关注

    First, drop gopkg.in/mgo.v2, it's obsolte, unmaintained. Instead use the community supported fork: github.com/globalsign/mgo.

    Next, you should first read the public, package documentation, and only "revert" to reading the source to clear such things up if the documentation does not offer the answers. But drawing conclusions from the source is always dangerous, as the implementation may change at any time, only what is documented is guaranteed. Documentation of mgo.Session states:

    All Session methods are concurrency-safe and may be called from multiple goroutines.

    This is all the guarantee you have, and all you should depend on. Using methods of mgo.Collection may or may not be safe for concurrent use, so do not do that. When needed, always obtain a "new" collection from the session, as that is safe to be accessed from multiple goroutines.

    And now on to your actual problem.

    Your ApplicationState struct type contains a lock (sync.RWMutex), and you unmarshal your query result into the same ApplicationState value that holds the lock:

    func (state *ApplicationState) ReadStateMGO(c *mgo.Collection) error {
        state.lock.Lock()
        defer state.lock.Unlock()
        return c.Find( bson.M{} ).Select( bson.M{"_id": 0} ).One(state)
    }
    

    This is a red flag! Don't do this! Unmarshaling into a value may clear any fields, including the lock!

    Yes, you may say it's an unexported field, so the mgo package should not / could not change it. This is true, but the mgo package may decide to create a new ApplicationState value to unmarshal into, and the new value may be assigned to the value pointed by the passed pointer (state).

    If this happens, the newly created ApplicationState will have a lock field being its zero-value, which is an unlocked mutex. And once this happens, the subsequent unlock will obviously fail (panic).

    Solution? Move locks outside of struct values you intend to serialize / deserialize. Or at least don't expect the lock state to transfer along with other fields.

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

报告相同问题?

悬赏问题

  • ¥15 oracle集群安装出bug
  • ¥15 关于#python#的问题:自动化测试
  • ¥20 问题请教!vue项目关于Nginx配置nonce安全策略的问题
  • ¥15 教务系统账号被盗号如何追溯设备
  • ¥20 delta降尺度方法,未来数据怎么降尺度
  • ¥15 c# 使用NPOI快速将datatable数据导入excel中指定sheet,要求快速高效
  • ¥15 再不同版本的系统上,TCP传输速度不一致
  • ¥15 高德地图2.0 版本点聚合中Marker的位置无法实时更新,如何解决呢?
  • ¥15 DIFY API Endpoint 问题。
  • ¥20 sub地址DHCP问题