【问题标题】:Managing slices with mutex for performance in golang在 golang 中使用互斥体管理切片以提高性能
【发布时间】:2026-01-29 01:50:01
【问题描述】:

我有一个结构如下。

type User struct {
    Mutex sync.RWMutex
    Username string
    Stuff map[string]string
}

我也有以下全局。

var MyUsers []User
var UserMutex sync.RWMutex

由于我的应用程序每秒访问MyUsers 切片数千次,我认为这将是管理性能的最佳方式。如果我想修改一个单独的 MyUsers 元素,那么我将锁定该特定元素:

MyUsers[x].Mutex.Lock()

然后进行任何修改并解锁它。

我的问题是,这样做安全吗?我有普通的全局UserMutex 仅在附加到切片时锁定切片......但是当附加到切片时,所有现有元素是否都在内存中移动?

如果一个线程同时附加到切片,而另一个线程正在修改一个元素,会发生什么?这可以在上面的设置中发生吗?

我是否可以使用make() 分配足够大的切片,以便任何append() 都不会影响内存中的位置?

【问题讨论】:

  • 永远,从字面上永远永远不会将 sync.WhateverMutex 设置为结构的字段。始终使用 *sync.WahteverMutex,因为您必须 复制sync.Mutex。并且在追加到切片期间可能会复制元素。另外:您必须在追加时防止 所有 修改,因此个人保护必须包括对 MyUsers 的读取锁定。也许你应该重新设计,但很难说。
  • @Volker:互斥量值非常常见和惯用,无论是嵌入的还是作为命名字段。此外,go vet 警告如果您不使用指向结构的指针,则复制互斥锁值。我认为整个 std 库中实际上没有一个 *sync.Mutex 字段。
  • @JimB 是的,如果包含 Mutex 的结构总是通过指针传递,那很好。 OP 的 var MyUsers []Uservar MyUsers []User 不属于此类别。我承认这个建议太强烈了。
  • @Volker: 是的,[]User 类型是这里的问题,尽管在复制互斥体时完全编写 any 测试会失败,所以我希望有一个永远不会走得太远。

标签: go memory mutex


【解决方案1】:

正如您已经注意到的,这是不安全的,因为append 可能需要在超出容量时分配一个新数组,这将导致元素被复制到新数组中。

但是,您可以在 MyUsers 切片中存储指针而不是值:

var MyUsers []*User

如果您这样做,那么分配新数组时不会出现问题,因为指针将被移动,但仍指向相同的底层 User 结构。

此外,如 cmets 中所述,最好在 User 结构中使用指向 Mutex 的指针以防万一:

Mutex *sync.RWMutex

但是,如果您将代码设计为永远不会复制 User 对象并且始终传递指针,那就更好了。

【讨论】:

  • 感谢您的建议。我已将切片更改为存储指针而不是值,我还将互斥锁更改为结构内的指针。这篇文章和上面的每个人(也感谢 Jim 和 Volker)让我理解并教会了我,我应该真正考虑在这种情况下使用什么类型,而不是在没有真正考虑每个具体的情况下匆忙写出字段字段以及它的用途。