我有一个并发实用程序,它本质上是一个无缓冲的包装器
渠道 - 提供Send
和Receive
功能以及可选的超时时间
context.Context
。因此,函数内部的外观为select
大致如此(简化为博览会):
func Send(ctx context.Context, ch chan interface{}, item interface{}) error {
select {
case <-ctx.Done():
return ctx.Err()
case ch <- item:
return nil
}
}
此代码已经过测试并正常运行,但是如果有的话,则存在不良行为
上下文已被取消和可以写入通道,发送
将以非确定性成功或失败。这是预期的行为
select
,所以我在主select
之前添加了一个非阻止发送
预期的行为,如果陈,这第一个select
应该总是成功
是可用的。
func Send(ctx context.Context, ch chan interface{}, item interface{}) error {
select {
case ch <- item:
return nil
default:
}
select {
case <-ctx.Done():
return ctx.Err()
case ch <- item:
return nil
}
}
我合理地确信这是一个正确的实现,但我愿意 喜欢写一个试图运行事件排序的测试用例。 但是,由于处理多重问题所固有的非确定性 goroutines,我无法弄清楚如何正确地对事件进行排序。
以下是我想测试的内容:
func TestReceiveThenSend(t *testing.T) {
ch := make(chan interface{})
go Receive(context.Background(), ch)
// Somehow, wait until the above `Receive` is blocked reading from `ch`.
// Immediately expired context.
ctx, cancel := context.WithTimeout(context.Background(), 0)
defer cancel()
if err := Send(ctx, ch, "foo"); err != nil {
t.Fatal("expected Send to succeed because Receive is already called.")
}
}
显然,由于没有订购,我不能指望上述测试能够正常工作
在go Receive
和Send
之间。我尝试过各种各样的黑客攻击
runtime.Gosched
,time.Sleep
或实施知道的自定义Context
何时使用Done
成员。这些都不能强迫我订购
我想。
我被卡住了,我正在寻找任何方式,非常或丑陋,以强制这种行为。 有没有人有解决这个问题的技巧?