我对在 Go 中使用 sync.Mutex
有点困惑。我理解基本概念(调用 Lock()
将阻止其他 goroutines 执行它和 Unlock()
之间的代码),但我不确定我是否 需要这里。我已经看到了相当多的 C++ 答案,但在每个示例中,它们似乎都在直接修改和访问变量。
这是我的代码,来自一个名为配置的包,我将在整个应用程序中使用它来获取(令人惊讶的)配置和设置信息。
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 都可以随时调用它,尽管这种情况很少见。
鉴于此,我是否正确使用了它们?为什么在读取数据副本时需要一个(如果需要)?
最佳答案
当您读取可以同时写入的数据时,您需要一个互斥量。否则可能会发生边读边写,得到一半旧数据和一半新数据的情况。
所以你的例子似乎很好。因为您可能经常阅读配置但很少编写配置,所以您对 RWLock
的使用很有意义。这意味着只要不写入,多个线程可以同时读取。
在您的代码中看起来危险的是:
programConfig.Lock()
programConfig = newSettings
programConfig.Unlock()
因为 programConfig
包含 Mutex
你在不同的实例上执行 Lock
和 Unlock
这将导致僵局。您应该将 Mutex
添加到实例的“父级”。在这种情况下可能是包。
关于go - 如果我要返回变量的副本而不是指针,是否需要互斥体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41976512/