Go 初体验 - 并发与锁.3 - 竞态

时间:2021-07-25 20:34:56

竞态,就是多个协程同时访问临界区,由并发而产生的数据不同步的状态。

这个说的有点low,没办法,我就是这么表达的,官方的请度娘。

先上代码:

Go 初体验 - 并发与锁.3 - 竞态

输出:

Go 初体验 - 并发与锁.3 - 竞态

为何不是1000?就是因为竞态,发生竞态后,最终的输出是以最后一个协程执行的结果为准,但最后一个协程有一定的随机性,不是先跑先完。

改一下代码:

Go 初体验 - 并发与锁.3 - 竞态

输出:

Go 初体验 - 并发与锁.3 - 竞态

因为加了锁,这1000个协程是按照队列的顺序执行12行,所以稳定输出 final value of x 1000

再看:

Go 初体验 - 并发与锁.3 - 竞态

输出:

Go 初体验 - 并发与锁.3 - 竞态

照样稳定输出 final value of x 1000,因为信道的读和写都具有排他性,虽然不是锁住临界区,但是能起到让后来的协程排队的效果。

那么这两种情况怎么选择呢? 引用一个大牛的话:

Mutex vs 信道

通过使用 Mutex 和信道,我们已经解决了竞态条件的问题。那么我们该选择使用哪一个?答案取决于你想要解决的问题。如果你想要解决的问题更适用于 Mutex,那么就用 Mutex。如果需要使用 Mutex,无须犹豫。而如果该问题更适用于信道,那就使用信道。:)

由于信道是 Go 语言很酷的特性,大多数 Go 新手处理每个并发问题时,使用的都是信道。这是不对的。Go 给了你选择 Mutex 和信道的余地,选择其中之一都可以是正确的。

总体说来,当 Go 协程需要与其他协程通信时,可以使用信道。而当只允许一个协程访问临界区时,可以使用 Mutex。

就我们上面解决的问题而言,我更倾向于使用 Mutex,因为该问题并不需要协程间的通信。所以 Mutex 是很自然的选择。

我的建议是去选择针对问题的工具,而别让问题去将就工具。:)