首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用go通道使结构线程安全

使用go通道使结构线程安全
EN

Stack Overflow用户
提问于 2020-01-14 14:52:54
回答 2查看 1.1K关注 0票数 3

假设我有以下结构:

代码语言:javascript
复制
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:]...)
        }
    }
}

此模式不是线程安全的,因此以下测试由于某些争用条件(数组索引超出界限)而失败:

代码语言:javascript
复制
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)
}

我刚开始搜索,我想我应该使用频道(?)。因此,实现并行的一种方法是这样:

代码语言:javascript
复制
type ManagerA struct {
    Manager
    addStringChan chan string
    removeStringChan chan string
}

func NewManagerA() *ManagerA {
    ma := &ManagerA{
        addStringChan: make(chan string),
        removeStringChan: make(chan string),
    }
    go func () {
        for {
            select {
            case msg := <-ma.addStringChan:
                ma.AddString(msg)
            case msg := <-ma.removeStringChan:
                ma.RemoveString(msg)
            }
        }
    }()
    return ma
}

func (m* ManagerA) AddStringA(s string) {
    m.addStringChan <- s
}
func (m* ManagerA) RemoveStringA(s string) {
    m.removeStringChan <- s
}

我想公开一个类似于非并发示例的API,因此是AddStringA,RemoveStringA。

这似乎像预期的那样同时工作(虽然我想内部的goroutine也应该在某个时候退出)。我的问题是,有很多额外的样板:

channels

  • define
  • 需要定义并初始化带有选择
  • 映射函数的

内部goroutine循环,以引导调用。

对我来说有点过分了。有没有一种方法可以简化这个(重构/语法/库)?

我认为实现这一点的最好方法是使用Mutex来代替?但是,是否仍有可能简化这种样板?

EN

回答 2

Stack Overflow用户

发布于 2020-01-14 14:57:44

使用互斥对象是非常惯用的,如下所示:

代码语言:javascript
复制
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()
}

你可以用渠道来做这件事,但正如你所注意到的,这是很多额外的工作,但收获并不多。用互斥是我的建议!

票数 8
EN

Stack Overflow用户

发布于 2020-01-14 14:58:07

如果您只需要使对结构线程的访问安全,请使用互斥:

代码语言:javascript
复制
type Manager struct {
   sync.Mutex
   data []string
}

func (m *Manager) AddString(s string) {
    m.Lock()
    m.strings = append(m.strings, s)
    m.Unlock()
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59736242

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档