93人参与 • 2025-04-24 • Golang
go 语言的 sync 包提供了基本的同步原语,用于在并发编程中协调 goroutine 之间的操作。
互斥锁用于保护共享资源,确保同一时间只有一个 goroutine 可以访问。
特点:
lock() 和 unlock()import (
"fmt"
"sync"
"time"
)
func main() {
var mutex sync.mutex
counter := 0
for i := 0; i < 1000; i++ {
go func() {
mutex.lock()
defer mutex.unlock()
counter++
}()
}
time.sleep(time.second)
fmt.println("计数器:", counter)
}
当多个 goroutine 需要读取而很少写入时,读写锁比互斥锁更高效。
特点:
rlock()、runlock()、lock()、unlock()var rwmutex sync.rwmutex
var data map[string]string = make(map[string]string)
// 读取操作
func read(key string) string {
rwmutex.rlock()
defer rwmutex.runlock()
return data[key]
}
// 写入操作
func write(key, value string) {
rwmutex.lock()
defer rwmutex.unlock()
data[key] = value
}
等待组用于等待一组 goroutine 完成执行。
特点:
add()、done()、wait()add() 增加计数器,参数可为负数done() 等同于 add(-1),减少计数器wait() 阻塞直到计数器归零func main() {
var wg sync.waitgroup
for i := 0; i < 5; i++ {
wg.add(1) // 增加计数器
go func(id int) {
defer wg.done() // 完成时减少计数器
fmt.printf("工作 %d 完成\n", id)
}(i)
}
wg.wait() // 等待所有 goroutine 完成
fmt.println("所有工作已完成")
}
once 确保一个函数只执行一次,无论有多少 goroutine 尝试执行它。
特点:
do(func())var once sync.once
var instance *singleton
func getinstance() *singleton {
once.do(func() {
instance = &singleton{}
})
return instance
}
条件变量用于等待或宣布事件的发生。
特点:
sync.newcond(&mutex)wait()、signal()、broadcast()wait() 自动解锁并阻塞,被唤醒后自动重新获取锁signal() 唤醒一个等待的 goroutinebroadcast() 唤醒所有等待的 goroutinevar mutex sync.mutex
var cond = sync.newcond(&mutex)
var ready bool
func main() {
go producer()
// 消费者
mutex.lock()
for !ready {
cond.wait() // 等待条件变为真
}
fmt.println("数据已准备好")
mutex.unlock()
}
func producer() {
time.sleep(time.second) // 模拟工作
mutex.lock()
ready = true
cond.signal() // 通知一个等待的 goroutine
// 或使用 cond.broadcast() 通知所有等待的 goroutine
mutex.unlock()
}
对于简单的计数器或标志,可以使用原子操作包而不是互斥锁。
特点:
add、load、store、swap、compareandswapimport (
"fmt"
"sync/atomic"
"time"
)
func main() {
var counter int64 = 0
for i := 0; i < 1000; i++ {
go func() {
atomic.addint64(&counter, 1)
}()
}
time.sleep(time.second)
fmt.println("计数器:", atomic.loadint64(&counter))
}
go 1.9 引入的线程安全的 map。
特点:
store、load、loadorstore、delete、rangevar m sync.map
func main() {
// 存储键值对
m.store("key1", "value1")
m.store("key2", "value2")
// 获取值
value, ok := m.load("key1")
if ok {
fmt.println("找到键:", value)
}
// 如果键不存在则存储
m.loadorstore("key3", "value3")
// 删除键
m.delete("key2")
// 遍历所有键值对
m.range(func(key, value interface{}) bool {
fmt.println(key, ":", value)
return true // 返回 false 停止遍历
})
}
对象池用于重用临时对象,减少垃圾回收压力。
特点:
get() 和 put()new 函数来创建新对象var bufferpool = sync.pool{
new: func() interface{} {
return new(bytes.buffer)
},
}
func process() {
// 获取缓冲区
buffer := bufferpool.get().(*bytes.buffer)
buffer.reset() // 清空以便重用
// 使用缓冲区
buffer.writestring("hello")
// 操作完成后放回池中
bufferpool.put(buffer)
}
下面是一个综合示例,展示了多个同步原语的使用:
package main
import (
"fmt"
"sync"
"time"
)
type safecounter struct {
mu sync.mutex
wg sync.waitgroup
count int
}
func main() {
counter := safecounter{}
// 启动 5 个 goroutine 增加计数器
for i := 0; i < 5; i++ {
counter.wg.add(1)
go func(id int) {
defer counter.wg.done()
for j := 0; j < 10; j++ {
counter.mu.lock()
counter.count++
fmt.printf("goroutine %d: 计数器 = %d\n", id, counter.count)
counter.mu.unlock()
// 模拟工作
time.sleep(100 * time.millisecond)
}
}(i)
}
// 等待所有 goroutine 完成
counter.wg.wait()
fmt.println("最终计数:", counter.count)
}
使用 defer 解锁:确保即使发生错误也能解锁
mu.lock() defer mu.unlock()
避免锁的嵌套:容易导致死锁
保持临界区简短:锁定时间越短越好
基准测试比较:
内存对齐:
超时控制:
ctx, cancel := context.withtimeout(context.background(), 5*time.second)
defer cancel()
done := make(chan struct{})
go func() {
// 执行可能耗时的操作
mu.lock()
// ...
mu.unlock()
done <- struct{}{}
}()
select {
case <-done:
// 操作成功完成
case <-ctx.done():
// 操作超时
}到此这篇关于go语言中sync包使用方法的文章就介绍到这了,更多相关go语言sync包使用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论