douye2020 2017-02-01 09:46
浏览 9
已采纳

如果我要返回变量的副本而不是指针,是否需要互斥锁?

I'm a little confused about the use of sync.Mutex in Go. I understand the basic concept (calling Lock() will prevent other goroutines from executing the code between it and Unlock()), but I'm not sure if I need it here. I've seen a fair few C++ answers for this but in each example they all seem to be modifying and accessing a variable directly.

This is my code from a package called configuration, which I will use throughout the application to get (surprisingly) configuration and settings information.

package config

import (
    "encoding/json"
    "fmt"
    "os"
    "sync"

    log "github.com/sirupsen/logrus"
)

/*
ConfigurationError is an implementation of the error interface describing errors that occurred while dealing with this
package.
*/
type ConfigurationError string

/*
Error prints the error message for this ConfigurationError. It also implements the error interface.
*/
func (ce ConfigurationError) Error() string {
    return fmt.Sprintf("Configuration error: %s", string(ce))
}

/*
configuration is a data struct that holds all of the required information for setting up the program. It is unexported
to prevent other packages creating more instances. Other packages that need settings information should call Current()
to access a copy of the unexported programConfig package variable.
*/
type configuration struct {
    sync.RWMutex
    LogLevel               log.Level `json:"logLevel,omitempty"`     //Logging
    LogLocation            string    `json:"logLocation,omitempty"`  //-
    HttpPort               int       `json:"port,omitempty"`         //Web
    LoginUri               string    `json:"loginUri"`               //-
    WebBaseUri             string    `json:"webBaseUri"`             //-
    StaticBaseUri          string    `json:"staticBaseUri"`          //-
    ApiBaseUri             string    `json:"apiBaseUri"`             //-
    StaticContentLocalPath string    `json:"staticContentLocalPath"` //-
    MaxSimultaneousReports int       `json:"maxSimultaneousReports"` //Reporting
}

var programConfig configuration

/*
Current returns a copy of the currently loaded program configuration.
*/
func Current() configuration {
    programConfig.RLock()
    defer programConfig.RUnlock()
    return programConfig
}

/*
Load attempts to load a JSON settings file containing a representation of the Configuration struct. It will then set
the value of the package-level config struct to the loaded values. Some settings changes will require a restart of the
server application.

filepath - the full path of the settings file including a leading slash or drive name (depending on the OS).
*/
func Load(filepath string) error {

    //Open the file for reading.
    settingsFile, err := os.Open(filepath)
    if err != nil {
        return ConfigurationError(err.Error())
    }
    defer settingsFile.Close()

    //Decode JSON into package level var.
    decoder := json.NewDecoder(settingsFile)
    newSettings := configuration{}
    err = decoder.Decode(&newSettings)
    if err != nil {
        return ConfigurationError(err.Error())
    }

    programConfig.Lock() //I'm not 100% sure this is the correct use of a mutex for this situation, so check up on that.
    programConfig = newSettings
    programConfig.Unlock()
    return nil

}

As you can see, I've used mutex in two places.

  • In Current(). Do I need this here if the function is not returning a pointer but a copy of the programConfig variable? The only way the underlying package variable will be modified is through the Load() function.
  • In the Load() function. This can be called at any time by any goroutine, although it rarely will be.

Given that, am I using them correctly and why do I need one when reading a copy of the data (if so)?

  • 写回答

1条回答 默认 最新

  • duanlu1950 2017-02-01 10:37
    关注

    When you read data which can be written at the same time you need a mutex. Otherwise it might happen that you read while it's written and get half of the old data and half of the new data.

    So your example seems to be just fine. Because you are probably reading the config very often but writing it rarely your usage of a RWLock makes sense. This means that multiple threads can read at the same time as long as it's not written.

    What in your code looks dangerous is:

    programConfig.Lock()
    programConfig = newSettings
    programConfig.Unlock()
    

    Because programConfig contains the Mutex you are doing the Lock and Unlock on different instances which will lead to deadlocks. You should add the Mutex to the "parent" of the instance. In this case probably the package.

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

报告相同问题?

悬赏问题

  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里
  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值