使用-race标志时,GO中的竞争条件意味着什么

时间:2018-10-19 13:31:34

标签: go concurrency race-condition

考虑以下代码:

package main

import (
    "fmt"
    "sync"
)

func main() {
var a int
m := new(sync.Mutex)

wg := sync.WaitGroup{}
wg.Add(2)

go func(){
    m.Lock()
    a = 2
    m.Unlock()
    wg.Done()
}()

go func(){
    //m.Lock()
    a = 9
    //m.Unlock()
    wg.Done()
}()

wg.Wait()

fmt.Println(a)
}

如果我们使用-race标志运行此代码,则会收到警告,提示存在竞争条件。

1)这种比赛情况可能会出什么问题?

如果我们取消注释第二个例程的锁,则不会收到竞争状况警告。但是我们可以有不同的输出,因此存在竞争条件。

2)为什么我们现在没有比赛条件警告?

2 个答案:

答案 0 :(得分:2)

1)此比赛条件可能会出错吗?

  • Undefined behavior
  • 内存损坏
  • 崩溃 从字面上看,这可能是最糟糕的结果:不知道变量p可能具有完全无效的值:

2)为什么我们现在没有比赛条件警告?

Mutex是可以保证原子性的原语。取消注释锁后,运行时/操作系统将完全同步对锁保护的语句的访问。即a将永远不会同时设置为2和9。

这里可能会对性能产生影响(您的应用可能永远不会遇到它们),因为它们是序列化的操作。通常这是一个很好的权衡,因为它可以确保正确性,但要以潜在的性能影响为代价。


go文档包含有关此问题的详细信息的丰富资源:

答案 1 :(得分:0)

当必须以正确的顺序执行两个或更多个操作时,就会出现种族条件,但是由于编写程序的方式,它们没有这样做。

数据竞争::一个并发操作试图读取变量,而在某个不确定的时间,另一个并发操作试图写入同一变量。

这个简单的示例具有数据竞争性,因为我们正在读取a并进行打印,而其他一些常规例程正在尝试更新该值。

package main

import "fmt"

func main() {
    var a int

    go func() {
        a++
    }()

    if a == 0 {
        fmt.Println("data:", a)
    }
}

输出可能性:

  1. a1,并被打印({if进入时,a0,但打印时已更改。)
  2. a0,并且已打印(尚未发生更新)
  3. 不打印任何内容,(a已更新)

出现您的问题:

1. 此比赛条件可能会出错吗?

回答:

    {li> a
  1. 输出不确定,可以是29。(如果您的第一个go func()首先运行,则将为9,如果第二func()首先运行,则a将是2)。
  2. 您在第二个a中更新func(),现在第一个func()运行并尝试更新,最后写入a的函数将确定输出。它可以是再次29
  3. 锁的使用假设是,在任何时候只有一个goroutine拥有它,并且当它具有访问权限时,因为没有得到排他锁,所以没有其他变量写入该锁,但是如果任何共享变量不受锁保护,然后可以访问变量并进行必要的更改。

2. 为什么我们现在没有比赛条件警告?

回答:

因为现在在任何时间变动中,只有一个goroutine可以访问a并对其进行更新。但是即使现在a也不是确定性的,func()最后运行的哪个将确定它。 (即使这是一种竞争条件,也可以通过适当的同步来克服这一情况)。

-race无法确定这种类型的race条件,因为几乎不可能这样做。