英文:
How to test Golang channels / go-routines
问题
我有一个包含一个字节数据的类型,并且接受一个通道来发布新数据。其他代码可以使用Read
函数读取最后写入的字节数据。
编辑:有关实际可运行的代码,请参见https://github.com/ariejan/i6502/pull/3,特别是acia6551.go和acia6551_test.go文件。测试结果可以在此处查看:https://travis-ci.org/ariejan/i6502/jobs/32862705
我有以下代码:
// 模拟某种串行接口芯片。
type Unit struct {
// 用于其他人使用的通道,写入的字节将放置在rxChar中
Rx chan byte
// 最后一个写入字节的内部存储
rxChar byte // 内部存储
}
// 用于内部读取存储在rxChar中的数据
func (u *Unit) Read() byte {
return u.rxChar
}
// 创建新的Unit并启动goroutine监听Rx字节
func NewUnit(rx chan byte) *Unit {
unit := &Unit{Rx: rx}
go func() {
for {
select {
case data := <-unit.Rx:
unit.rxChar = data
fmt.Printf("Posted 0x%02X\n", data)
}
}
}()
return unit
}
我的测试代码如下:
func TestUnitRx(t *testing.T) {
rx := make(chan byte)
u := NewUnit(rx)
// 向Rx通道发布一个字节
// 这将打印"Posted 0x42",正如你所期望的那样
rx <- 0x42
// 使用testing
// 应该读取最后一个字节,0x42,但失败了。
fmt.Println("Reading value...")
assert.Equal(t, 0x42, u.Read())
}
起初,我认为"Reading value"在goroutine写入数据之前发生。但是,"Posted"消息总是在"Reading"之前打印。
因此,还有两个问题:
- 这是处理传入字节流的最佳方式吗(以9600波特率)?
- 如果这是正确的方式,我应该如何正确测试它,或者我的代码有什么问题?
英文:
I have a type that contains a byte of data, and takes a channel to post new data there. Other code can read the last written byte of data using a Read
function.
Edit: for actual, runnable code, see https://github.com/ariejan/i6502/pull/3 especially files acia6551.go and acia6551_test.go. Tests results can be viewed here: https://travis-ci.org/ariejan/i6502/jobs/32862705
I have the following:
// 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
}
My test looks like this:
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())
}
At first I figured the "Reading value" happened before the go-routing got around to writing the data. But the "Posted" message is always printed before "Reading".
So, two questions remain:
- Is this the best way to handle an incoming stream of bytes (at 9600 baud ;-))
- If this is the right way, how can I properly test it or what is wrong with my code?
答案1
得分: 1
根据这里发布的代码片段来看,似乎没有任何保证访问存储数据时操作顺序的内容。你可以在任何在goroutine之间共享的数据周围使用互斥锁。
在这里,更好的选择是使用长度为1的缓冲通道来写入、存储和读取字节。
使用-race
选项对程序进行测试并使用竞争检测器是一个好主意。
由于这看起来非常像"流",你很可能希望进行一些缓冲,并查看一些关于io.Reader
和io.Writer
接口通常如何使用的示例。
英文:
Guessing by the pieces posted here, it doesn't look like you have anything guaranteeing the order of operations when accessing the stored data. You can use a mutex around any data shared between goroutines.
A better option here is to use buffered channels of length 1 to write, store, and read the bytes.
It's always a good idea to test your program with -race
to use the race detector.
Since this looks very "stream" like, you very well may want some buffering, and to look at some examples of how the io.Reader
and io.Writer
interfaces are often used.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论