2

我遇到了一个问题,如果它有一个导出的sync.Mutex. 如果我使互斥锁未导出(通过不将其大写),一切似乎都有效。我很想知道为什么会这样,并确保吞噬 unexported 没有其他问题sync.Mutex

我看到谷歌上有几个与 相关的问题sync.RWMutex,但没有一个真正解释为什么会发生这种情况。

package main

import (
    "sync"
    "encoding/gob"
    "os"
    "fmt"
)

func writeGob(filePath string, object interface{}) error {
    file, err := os.Create(filePath)
    defer file.Close()

    if err != nil {
        return err
    }

    encoder := gob.NewEncoder(file)
    err = encoder.Encode(object)
    return err
}

type Dog struct {
    Name string
    GobMux sync.Mutex
}

func main() {
    d := &Dog{Name: "Fido"}
    err := writeGob("./gob", d)
    fmt.Printf("Error: %v\n", err)
}

输出:

错误:gob:类型 sync.Mutex 没有导出字段

4

1 回答 1

1

Gob 编码

如果我使互斥锁未导出(通过不将其大写),一切似乎都有效。我很想知道为什么会这样。

正如Cerise 提到的,这有一个未解决的问题,但简而言之,如果您尝试对没有导出字段的结构(例如互斥锁)进行编码,这通常是一个编程错误。

不过,有一些方法可以解决这个特定问题。

您可以将互斥锁设为私有并将锁定/解锁包装在公共函数中,而不是进入结构来操作互斥锁。例如

func (d *Dog) SetName(name string) {
    d.GobMux.Lock()
    d.Name = name
    d.GobMux.Unlock()
}

您还可以包装类型并将互斥锁拉出:

type Dog struct {
    Name string
}

type DogWrapper struct {
    Dog *Dog
    GobMux sync.Mutex
}

如果您有许多小结构,这相当麻烦,但对于较少数量的更复杂的结构,它可能没问题。

最后,解决这个问题的“正确”方法是编写自己的 GobEncode/Decode 例程。stdlib 中有一些稀疏示例,例如 time.GobEncode,但通常这似乎需要相当多的工作。

一般互斥锁

...并确保在获取未导出的 sync.Mutex 时没有其他问题。

互斥锁与 Go 运行时的进程内内存和调度程序紧密耦合。它们帮助 Go 运行时决定允许哪些 goroutine 读取或写入内存的特定部分,并决定何时可以调度这些 goroutine(即等待互斥锁解锁的 goroutine 在互斥锁解锁之前不会被调度) .

如果您使用 Gob 将数据结构复制到另一个进程,则接收进程的运行时与发送 gob 的进程相比具有完全不同的内部状态,因此互斥锁无法进行逻辑传输。将互斥体复制到另一个进程有点像在火星上使用地球 GPS 坐标。他们只是不匹配。

当我从 gob 读回 Dog 对象时,看起来互斥锁已解锁,而不管互斥锁在保存到 gob 时的状态如何。我可以指望这种行为吗?

正如 Mutex 的文档所述,“Mutex 的零值是一个未锁定的互斥锁。” 所以是的,你可以依赖这种行为。

其他编码器

在我看来,尽管 gob 存在于 stdlib 中,但它并没有受到太多关注,因为还有许多其他成熟的编码选项可用。如果 gob 不能满足您的需求,还有许多其他可用选项——JSON、Cap'n Proto、net/rpc 等,它们具有不同的特性,可能更适合您。

于 2019-05-21T19:50:57.290 回答