Mutex 是一种用作锁定机制的方法,以确保在任何时间点只有一个 Goroutine 访问代码的临界区。这样做是为了防止发生竞争条件。同步包包含互斥锁。 Mutex 上定义的两个方法
- 锁
- 开锁
调用 Lock 和 Unlock 之间的任何代码都将仅由一个 Goroutine 执行。
mutex.Lock()
x = x + 1 // this statement be executed
// by only one Goroutine
// at any point of time
mutex.Unlock()
如果一个 Goroutine 已经拥有锁,并且如果一个新的 Goroutine 试图获取锁,那么新的 Goroutine 将被停止,直到互斥锁被解锁。要理解这个概念,让我们首先了解一个具有竞争条件的程序。
具有竞争条件的程序
这是一个遇到竞争条件的程序示例
// Program with race condition
package main
import (
"fmt"
"sync" // to import sync later on
)
var GFG = 0
// This is the function we’ll run in every
// goroutine. Note that a WaitGroup must
// be passed to functions by pointer.
func worker(wg *sync.WaitGroup) {
GFG = GFG + 1
// On return, notify the
// WaitGroup that we’re done.
wg.Done()
}
func main() {
// This WaitGroup is used to wait for
// all the goroutines launched here to finish.
var w sync.WaitGroup
// Launch several goroutines and increment
// the WaitGroup counter for each
for i := 0; i < 1000; i++ {
w.Add(1)
go worker(&w)
}
// Block until the WaitGroup counter
// goes back to 0; all the workers
// notified they’re done.
w.Wait()
fmt.Println("Value of x", GFG)
}
说明:在上面的程序中,第 1 行的worker函数。 12 将GFG的值增加 1,然后在WaitGroup上调用Done()以通知其完成。工作函数被调用 1000 次。这些 Goroutines 中的每一个都同时运行,并且在尝试增加第 1 行的GFG时会发生竞争条件。 13 因为多个 Goroutine 尝试同时访问GFG的值。由于竞争条件,多次运行相同的程序每次都会给出不同的输出。
Remember one thing, if you will check the output of the above program using the online compiler, you might get the same output every time(means no race condition) due to the deterministic nature. So use the local compiler like Visual Studio or CMD to see the results.
使用互斥体解决上述问题
这是一个如何修复竞争条件的程序示例。
// Program with race condition fixed by mutex
package main
import (
"fmt"
"sync" // to import sync later on
)
var GFG = 0
// This is the function we’ll run in every
// goroutine. Note that a WaitGroup must
// be passed to functions by pointer.
func worker(wg *sync.WaitGroup, m *sync.Mutex) {
// Lock() the mutex to ensure
// exclusive access to the state,
// increment the value,
// Unlock() the mutex
m.Lock()
GFG = GFG + 1
m.Unlock()
// On return, notify the
// WaitGroup that we’re done.
wg.Done()
}
func main() {
// This WaitGroup is used to wait for
// all the goroutines launched here to finish.
var w sync.WaitGroup
// This mutex will synchronize access to state.
var m sync.Mutex
// Launch several goroutines and increment
// the WaitGroup counter for each
for i := 0; i < 1000; i++ {
w.Add(1)
go worker(&w, &m)
}
// Block until the WaitGroup counter
// goes back to 0; all the workers
// notified they’re done.
w.Wait()
fmt.Println("Value of x", GFG)
}
输出:
x 1000 的值
说明:互斥为结构类型和互斥在管线创建任何类型的变量m。 31. 更改了工作函数,以便在第 1 行中增加GFG的代码。 18 在m.Lock()和m.Unlock() 之间。现在在任何时间点都只允许一个 Goroutine 执行这段代码,从而处理竞争条件。
互斥锁的地址必须在行号中传递。 37. 如果互斥量是按值传递的,那么每个 Goroutine 将拥有自己的互斥量副本,并且竞争条件仍然存在。