【问题标题】:How to test Golang channels / go-routines如何测试 Golang 通道/go-routines
【发布时间】:2014-10-11 11:22:36
【问题描述】:

我有一个包含一个字节数据的类型,并采用一个通道来在那里发布新数据。其他代码可以使用Read 函数读取最后写入的数据字节。

编辑:对于实际的、可运行的代码,请参阅https://github.com/ariejan/i6502/pull/3,尤其是文件 acia6551.go 和 acia6551_test.go。测试结果可以在这里查看:https://travis-ci.org/ariejan/i6502/jobs/32862705

我有以下几点:

// Emulates a serial interface chip of some kind.
type Unit struct {
  // Channel used for others to use, bytes written here will be placed in rxChar
  Rx chan byte

  // Internal store of the last byte written.
  rxChar byte // Internal storage
}

// Used internally to read data store in rxChar
func (u *Unit) Read() byte {
  return u.rxChar
}

// Create new Unit and go-routing to listen for Rx bytes
func NewUnit(rx chan byte) *Unit {
  unit := &Unit{Rx: rx}

  go func() {
    for {
      select {
      case data := <-unit.Rx:
        unit.rxData = data
        fmt.Printf("Posted 0x%02X\n", data)
      }
    }
  }()

  return unit
}

我的测试如下所示:

func TestUnitRx(t *testing.T) {
  rx := make(chan byte)
  u := NewUnit(rx)

  // Post a byte to the Rx channel
  // This prints "Posted 0x42", as you'd expect
  rx <- 0x42

  // Using testing
  // Should read last byte, 0x42 but fails.
  fmt.Println("Reading value...")
  assert.Equal(t, 0x42, u.Read()) 
}

起初我认为“读取值”发生在 go-routing 开始写入数据之前。但是“已发布”消息总是在“正在阅读”之前打印。

所以,还有两个问题:

  • 这是处理传入字节流的最佳方式吗(9600 波特 ;-))
  • 如果这是正确的方法,我该如何正确测试它或我的代码有什么问题?

【问题讨论】:

  • 请修正您的代码。您缺少右括号,并且 rxChar 在您的 Read 方法中未声明。 rxData 应该是 rxChar 吗?
  • @JimB 修复了我的代码。
  • 如果你发布了一些可以运行的东西会有所帮助。没有办法知道 rxData 和 rxChar 之间发生了什么。
  • goroutine 中有这么多代码。它大约相当于go func() { for unit.rxData = range unit.Rx { fmt.Printf("Posted 0x%02X\n", data) }(除了如果你关闭频道会正确终止)
  • 我 +1 @Dustin。同样在您的 github 提交中。我建议在您的实施中根本不使用渠道。只需从所有这些串行接口中创建一个实现 io.Readable 的类。

标签: testing concurrency go testify


【解决方案1】:

根据此处发布的内容进行猜测,您在访问存储的数据时似乎无法保证操作顺序。您可以在 goroutine 之间共享的任何数据周围使用互斥锁。

这里更好的选择是使用长度为 1 的缓冲通道来写入、存储和读取字节。

使用-race 测试您的程序以使用竞争检测器总是一个好主意。

因为这看起来很像“流”,所以您可能需要一些缓冲,并查看一些如何经常使用 io.Readerio.Writer 接口的示例。

【讨论】:

  • 没错,这就是我试图在我的问题中描述的内容。但是我该如何实现呢?
  • 使用通道或互斥锁(查看编辑是否有帮助)。
  • 顺便说一句 - 我更喜欢这个视频中描述的频道扇形:youtube.com/watch?v=QDDwwePbDtw 依赖于其他同步机制,如互斥锁。如果你确定来自另一种语言,它们更容易推理(我来自 C# 也是一样)——但该视频中描述的技术在 Go 生态系统中更有意义。只是把它留在这里作为替代方案。
  • 事实证明,让 Acia6551(或 Unit)实现 io.Reader 和 io.Writer 对我来说要好得多。感谢您指出这一点并告诉我-race,这实际上非常有用。
  • 除了 JimB 所写的内容之外,一个更好的想法是在开发过程中使用 无缓冲 通道。如果您没有遇到无缓冲通道的死锁,那么如果您稍后添加缓冲(添加缓冲只是一个优化步骤),您仍然不会。这种策略是一个很好的方法,可以清楚地考虑设计中的并发性,尤其是摆脱经常导致死锁的循环依赖。
猜你喜欢
  • 1970-01-01
  • 2021-09-09
  • 1970-01-01
  • 1970-01-01
  • 2021-07-29
  • 1970-01-01
  • 2016-04-02
  • 2018-12-15
  • 1970-01-01
相关资源
最近更新 更多