2

此问题的一个示例是用户创建资源并删除资源时。我们将执行该操作并增加(减少)一个计数器缓存。

在测试中,有时会出现竞争条件,即计数器缓存没有被 go 例程更新。

编辑:抱歉混淆,澄清:计数器缓存不在内存中,它实际上是数据库中的一个字段。竞争条件不是内存中的变量,实际上是 goroutine 写入数据库本身可能很慢!

我目前在操作后使用 1 秒睡眠,以确保在测试计数器缓存之前已更新计数器缓存。是否有另一种方法来测试 goroutine 而无需任意 1 秒的睡眠来等待 goroutine 完成?

干杯

4

2 回答 2

3

在测试中,有时会出现竞争条件,即计数器缓存没有被 go 例程更新。我目前在操作后使用 1 秒睡眠,以确保在测试计数器缓存之前已更新计数器缓存。

哎呀,我不想这么说,但你做错了。Go 具有一流的功能,使并发变得容易!如果您正确使用它们,就不可能有竞争条件。

事实上,有一个工具可以为您检测种族。我敢打赌它抱怨你的程序。

一个简单的解决方案:

  • 让主例程创建一个 goroutine 来跟踪计数器。
  • goroutine 只会做一个选择并获得一条消息来增加/减少或读取计数器。(如果读取,会传入一个channel返回数字)
  • 当您创建/删除资源时,通过它的通道向 goroutine 计数器发送适当的消息。
  • 当你想读取计数器时,发送一条消息进行读取,然后读取返回通道。

(另一种选择是使用锁。它的性能会更高一点,但编写和确保它是正确的会更麻烦。)

于 2013-09-23T14:36:24.367 回答
0

一种解决方案是让您的柜台提供一个频道,该频道会在值更改后立即更新。在 go 中,通常的做法是通过传达结果来进行同步。例如,您 Couter可能看起来像这样:

type Counter struct {
   value int
   ValueChange chan int
}

func (c *Counter) Change(n int) {
    c.value += n
    c.ValueChange <- c.value
}

每当Change被调用时,新值都会通过通道传递,等待该值的人会解除阻塞并继续执行,因此与计数器同步。使用此代码,您可以监听ValueChange如下更改:

v := <-c.ValueChange

并发调用c.Change不再是问题。

play 上有一个可运行的示例

于 2013-09-23T14:50:41.737 回答