sync.Mutex
sync.Mutex
是Go语言中的互斥锁,它用于在多个goroutine之间提供对共享资源的互斥访问,以确保同一时间只有一个goroutine可以访问共享资源。
sync.Mutex
类型的定义如下:
type Mutex struct {
state int32
sema uint32
}
sync.Mutex
结构体中包含了两个字段:
state
字段是一个int32类型的标志,用于表示锁的状态。0表示未锁定,1表示已锁定。sema
字段是一个uint32类型的信号量,用于控制goroutine的调度和阻塞。
sync.Mutex
提供了两个方法:
Lock
:获取互斥锁。如果锁已经被其他goroutine获取,当前goroutine将被阻塞,直到锁被释放。Unlock
:释放互斥锁。如果当前goroutine未持有锁,调用Unlock
将会导致运行时错误。
下面是一个使用sync.Mutex
的示例:
package main
import (
"fmt"
"sync"
)
func main() {
var mutex sync.Mutex
counter := 0
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
mutex.Lock()
counter++
fmt.Println("Counter:", counter)
mutex.Unlock()
}()
}
wg.Wait()
fmt.Println("Program finished.")
}
在上面的示例中,我们创建了一个sync.Mutex
实例mutex
,然后启动了10个goroutine。每个goroutine首先调用mutex.Lock()
获取互斥锁,然后对共享的counter
变量进行加1操作,并打印出counter
的值,最后调用mutex.Unlock()
释放互斥锁。
运行示例会输出以下内容:
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Counter: 6
Counter: 7
Counter: 8
Counter: 9
Counter: 10
Program finished.
从输出可以看出,虽然有10个goroutine同时对counter
变量进行加1操作,但由于使用了互斥锁,每次只有一个goroutine能够获取到锁并修改counter
的值,从而保证了数据的一致性。
如果没有使用sync.Mutex,就难于保证数据的一致性
package main
import (
"fmt"
"sync"
)
func main() {
var mutex sync.Mutex
counter := 0
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
// mutex.Lock()
counter++
fmt.Println("Counter:", counter)
// mutex.Unlock()
}()
}
wg.Wait()
fmt.Println("Program finished.")
}
Counter: 1
Counter: 3
Counter: 4
Counter: 5
Counter: 6
Counter: 7
Counter: 8
Counter: 9
Counter: 10
Counter: 2
Program finished.
需要注意的是,在使用sync.Mutex
时,我们必须遵循以下规则:
- 在每个对共享资源的修改操作之前,必须调用
Lock
方法获取锁。 - 在对共享资源的修改操作完成后,必须调用
Unlock
方法释放锁。 - 在持有锁期间,应尽量避免长时间的阻塞或耗时操作,以免影响其他goroutine的执行。
- 不应该对已经持有的锁再次调用
Lock
方法,这将导致死锁。 - 一般情况下,应尽量将锁的作用范围限制在最小的代码块中,以减少竞争和锁的持有时间。
使用互斥锁时,需要谨慎处理竞争条件和死锁问题,确保正确地获取和释放锁,以避免数据的不一致性和程序的死锁。