Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go语言sync包使用

Go语言中sync包使用方法教程

作者:Clown95

在Go语言的并发编程实践中,性能优化总是绕不开的话题,下面这篇文章主要介绍了Go语言中sync包使用方法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

Go 语言的 sync 包提供了基本的同步原语,用于在并发编程中协调 goroutine 之间的操作。

1. 互斥锁 (Mutex)

互斥锁用于保护共享资源,确保同一时间只有一个 goroutine 可以访问。

特点:

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)
}

2. 读写锁 (RWMutex)

当多个 goroutine 需要读取而很少写入时,读写锁比互斥锁更高效。

特点:

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
}

3. 等待组 (WaitGroup)

等待组用于等待一组 goroutine 完成执行。

特点:

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("所有工作已完成")
}

4. 一次性执行 (Once)

Once 确保一个函数只执行一次,无论有多少 goroutine 尝试执行它。

特点:

var once sync.Once
var instance *singleton

func getInstance() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}

5. 条件变量 (Cond)

条件变量用于等待或宣布事件的发生。

特点:

var 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()
}

6. 原子操作 (atomic)

对于简单的计数器或标志,可以使用原子操作包而不是互斥锁。

特点:

import (
    "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))
}

7. Map (sync.Map)

Go 1.9 引入的线程安全的 map。

特点:

var 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 停止遍历
    })
}

8. Pool (sync.Pool)

对象池用于重用临时对象,减少垃圾回收压力。

特点:

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)
}

9. 综合示例

下面是一个综合示例,展示了多个同步原语的使用:

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)
}

最佳实践

总结 

到此这篇关于Go语言中sync包使用方法的文章就介绍到这了,更多相关Go语言sync包使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文