- 使用
sync.Mutex
保护共享数据
sync.Mutex
是Go语言中用于同步访问共享资源的基础工具。通过锁定和解锁互斥锁,可以确保在同一时间只有一个goroutine能够访问共享数据,从而避免竞态条件。
- 示例代码:
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
numGoroutines := 1000
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Final counter value:", counter)
}
- 使用
sync.RWMutex
进行读写分离(适用于读多写少场景)
sync.RWMutex
允许多个goroutine同时进行读操作,但只允许一个goroutine进行写操作。这样在读多写少的场景下,可以提高程序的并发性能。
- 示例代码:
package main
import (
"fmt"
"sync"
)
var (
data = make(map[string]int)
rwmu sync.RWMutex
)
func read(key string, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
value := data[key]
fmt.Printf("Read value for key %s: %d\n", key, value)
rwmu.RUnlock()
}
func write(key string, value int, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
data[key] = value
fmt.Printf("Write value %d for key %s\n", value, key)
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go write("test", 42, &wg)
go read("test", &wg)
wg.Wait()
}
- 避免死锁
- 死锁原因:死锁通常发生在两个或多个goroutine相互等待对方释放锁的情况下。
- 避免方法:
- 按照相同的顺序获取锁。例如,如果有两个锁
mu1
和mu2
,在所有的goroutine中都先获取mu1
再获取mu2
,这样可以避免死锁。
- 使用
context.Context
来设置操作的超时。如果一个goroutine在获取锁时等待时间过长,可以通过context.Context
的超时机制来取消操作,从而避免死锁。
- 示例代码(使用
context.Context
避免死锁):
package main
import (
"context"
"fmt"
"sync"
"time"
)
var mu sync.Mutex
func worker(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Operation cancelled due to timeout")
return
default:
}
// 尝试获取锁,设置超时
ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer cancel()
select {
case <-ctx.Done():
fmt.Println("Timeout waiting for lock")
return
default:
mu.Lock()
defer mu.Unlock()
fmt.Println("Lock acquired, doing work...")
time.Sleep(200 * time.Millisecond)
}
}
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
wg.Add(2)
go func() {
defer wg.Done()
worker(ctx)
}()
go func() {
defer wg.Done()
mu.Lock()
time.Sleep(300 * time.Millisecond)
mu.Unlock()
}()
time.Sleep(200 * time.Millisecond)
cancel()
wg.Wait()
}
- 使用
sync.Cond
进行条件等待
sync.Cond
用于在某些条件满足时通知goroutine。例如,当共享数据达到某个状态时,唤醒等待的goroutine。
- 示例代码:
package main
import (
"fmt"
"sync"
"time"
)
var (
cond = sync.NewCond(&sync.Mutex{})
ready = false
)
func waiter() {
cond.L.Lock()
for!ready {
cond.Wait()
}
fmt.Println("Waiter got signal, doing work...")
cond.L.Unlock()
}
func signaler() {
time.Sleep(2 * time.Second)
cond.L.Lock()
ready = true
fmt.Println("Signaler setting ready to true and broadcasting")
cond.Broadcast()
cond.L.Unlock()
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
waiter()
}()
go func() {
defer wg.Done()
signaler()
}()
wg.Wait()
}