考虑以下代码:
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)为什么我们现在没有比赛条件警告?
答案 0 :(得分:2)
1)此比赛条件可能会出错吗?
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)
}
}
输出可能性:
a
为1
,并被打印({if进入时,a
为0
,但打印时已更改。)a
是0
,并且已打印(尚未发生更新)a
已更新)出现您的问题:
1. 此比赛条件可能会出错吗?
回答:
a
的2
或9
。(如果您的第一个go func()
首先运行,则将为9
,如果第二func()
首先运行,则a
将是2
)。a
中更新func()
,现在第一个func()
运行并尝试更新,最后写入a
的函数将确定输出。它可以是再次2
或9
。2. 为什么我们现在没有比赛条件警告?
回答:
因为现在在任何时间变动中,只有一个goroutine可以访问a
并对其进行更新。但是即使现在a
也不是确定性的,func()
最后运行的哪个将确定它。 (即使这是一种竞争条件,也可以通过适当的同步来克服这一情况)。
-race
无法确定这种类型的race
条件,因为几乎不可能这样做。