【问题标题】:How to use channels to safely synchronise data in Go如何在 Go 中使用通道安全地同步数据
【发布时间】:2016-10-19 14:10:29
【问题描述】:

以下是如何使用互斥锁来安全访问数据的示例。我将如何使用 CSP (communication sequential processes) 而不是使用互斥锁和解锁来做同样的事情?

type Stack struct {
  top    *Element
  size   int
  sync.Mutex
}

func (ss *Stack) Len() int {
  ss.Lock()
  size := ss.size
  ss.Unlock()
  return size

}

func (ss *Stack) Push(value interface{}) {
  ss.Lock()
  ss.top = &Element{value, ss.top}
  ss.size++
  ss.Unlock()
}

func (ss *SafeStack) Pop() (value interface{}) {
  ss.Lock()
  size := ss.size
  ss.Unlock()
  if size > 0 {
    ss.Lock()
    value, ss.top = ss.top.value, ss.top.next
    ss.size--
    ss.Unlock()
    return
  }

  return nil
}

【问题讨论】:

  • 这是一道作业题吗?您的代码似乎缺少部分内容,包括 Element 的定义。
  • 为什么?在这种情况下,互斥锁是正确的原语吗? (另外,使用defer play.golang.org/p/G-Y9sGooaQ 更简洁,更不容易出错)
  • 通道范式的全部目的是摆脱互斥体。使用通道,您可以在没有它们的情况下进行同步。但为了向您展示,我们需要您的实际代码,而不仅仅是锁定代码。通常为了有效地使用渠道,必须围绕它设计程序架构。你不能仅仅改变几行来有效地使用它们。
  • 你的代码中有一个竞争条件——当你释放互斥锁来测试你的堆栈是否为空时,另一个 goroutine 可能会弹出最后一个元素。

标签: go mutex


【解决方案1】:

如果您真的要查看 Go 是如何实现通道的,您实际上会看到一个数组周围的互斥体,其中包含一些额外的线程处理来阻止执行,直到值被传递。通道的工作是将数据从内存中的一个位置轻松移动到另一个位置。因此,在您有锁和解锁的地方,您会有类似以下示例的内容:

func example() {
    resChan := make(int chan)
    go func(){
        resChan <- 1
    }()
    go func(){
        res := <-resChan
    }
}

所以在示例中,第一个 goroutine 在发送值后被阻塞,直到第二个 goroutine 从通道中读取。

要在 Go 中使用互斥体执行此操作,可以使用 sync.WaitGroup,它会在设置值时将一个添加到组中,然后将其从组中释放,第二个 goroutine 将锁定然后解锁该值。

您的示例中的奇怪之处是 1 没有 goroutine,所以这一切都发生在一个主 goroutine 中,并且锁被更传统地使用(如在 c 线程中),因此通道不会真正完成任何事情。您的示例将被视为反模式,就像 golang 谚语所说的“不要通过共享内存进行通信,而是通过通信来共享内存。”

【讨论】:

    猜你喜欢
    • 2018-05-29
    • 2021-12-05
    • 2016-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 2018-02-18
    • 1970-01-01
    相关资源
    最近更新 更多