假设我有以下结构:
package manager
type Manager struct {
strings []string
}
func (m *Manager) AddString(s string) {
m.strings = append(m.strings, s)
}
func (m *Manager) RemoveString(s string) {
for i, str := range m.strings {
if str == s {
m.strings = append(m.strings[:i], m.strings[i+1:]...)
}
}
}
Run Code Online (Sandbox Code Playgroud)
此模式不是线程安全的,因此以下测试由于某些竞争条件(数组索引超出范围)而失败:
func TestManagerConcurrently(t *testing.T) {
m := &manager.Manager{}
wg := sync.WaitGroup{}
for i:=0; i<100; i++ {
wg.Add(1)
go func () {
m.AddString("a")
m.AddString("b")
m.AddString("c")
m.RemoveString("b")
wg.Done()
} ()
}
wg.Wait()
fmt.Println(m)
}
Run Code Online (Sandbox Code Playgroud)
我是 Go 的新手,通过谷歌搜索,我想我应该使用渠道 (?)。因此,使这种并发的一种方法是这样的:
func TestManagerConcurrently(t *testing.T) {
m := &manager.Manager{}
wg := sync.WaitGroup{}
for i:=0; i<100; i++ {
wg.Add(1)
go func () {
m.AddString("a")
m.AddString("b")
m.AddString("c")
m.RemoveString("b")
wg.Done()
} ()
}
wg.Wait()
fmt.Println(m)
}
Run Code Online (Sandbox Code Playgroud)
我想公开一个类似于非并发示例的 API,因此是 AddStringA、RemoveStringA。
这似乎同时按预期工作(尽管我猜内部 goroutine 也应该在某个时候退出)。我的问题是有很多额外的样板:
对我来说似乎有点多。有没有办法简化这个(重构/语法/库)?
我认为实现这一点的最佳方法是改用互斥锁?但是还有可能简化这种样板吗?
使用互斥锁将是非常惯用的,如下所示:
type Manager struct {
mu sync.Mutex
strings []string
}
func (m *Manager) AddString(s string) {
m.mu.Lock()
m.strings = append(m.strings, s)
m.mu.Unlock()
}
func (m *Manager) RemoveString(s string) {
m.mu.Lock()
for i, str := range m.strings {
if str == s {
m.strings = append(m.strings[:i], m.strings[i+1:]...)
}
}
m.mu.Unlock()
}
Run Code Online (Sandbox Code Playgroud)
您可以使用渠道来做到这一点,但正如您所指出的,这是一项额外的工作,但收益不大。我的建议是使用互斥锁!
如果您只需要访问 struct 线程安全,请使用互斥锁:
type Manager struct {
sync.Mutex
data []string
}
func (m *Manager) AddString(s string) {
m.Lock()
m.strings = append(m.strings, s)
m.Unlock()
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
338 次 |
| 最近记录: |