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

时间:2017-02-01 09:46:35

标签: go concurrency mutex

我对Go中sync.Mutex的使用感到有些困惑。我理解基本概念(调用Lock()将阻止其他goroutine在它和Unlock()之间执行代码),但我不确定我是否需要。我已经看到了相当多的C ++答案,但在每个例子中,他们似乎都在直接修改和访问变量。

这是我在一个名为configuration的软件包中的代码,我将在整个应用程序中使用它来获取(令人惊讶的)配置和设置信息。

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

}

如您所见,我在两个地方使用过互斥锁。

  • Current()。如果函数没有返回指针而是返回programConfig变量的副本,我是否需要这个?修改底层包变量的唯一方法是通过Load()函数。
  • Load()函数中。任何goroutine都可以随时调用它,尽管很少会这样。

鉴于此,我是否正确使用它们为什么在阅读数据副本时是否需要一个(如果是这样)?

1 个答案:

答案 0 :(得分:6)

当您读取可以同时写入的数据时,您需要一个互斥锁。否则,您可能会在写入时读取并获得一半旧数据和一半新数据。

所以你的例子似乎没问题。因为您可能经常阅读配置但很少编写它,您使用name.setText(curr.getProPic()); dt.setText("Date & Time : " + curr.getLevel() + " ," + curr.getPos()); venue.setText("Venue : " + curr.getName()); 是有道理的。这意味着只要没有写入多个线程就可以同时读取。

您的代码中看起来很危险的是:

RWLock

由于programConfig.Lock() programConfig = newSettings programConfig.Unlock() 包含programConfig,您在不同的实例上执行MutexLock,这将导致死锁。您应该将Unlock添加到" parent"实例。在这种情况下可能是包。