Go 语言中的锁

发布时间:2024年01月12日

1. 并发基础

并发是同时发生多个计算或事件的能力。并发通常通过同时执行多个任务或进程来实现,这些任务或进程共享相同的资源(例如内存或处理器)。并发使用的基本机制被称为锁。在Go语言中,锁是一个类型变量,它包含一个内部计数器,用于跟踪已获取的锁的数量。当一个goroutine获取一个锁时,它会将计数器增加一;当一个goroutine释放一个锁时,它会将计数器减少一。

2. 锁类型

Go语言中提供了五种类型的锁:互斥锁(mutex)、读写锁(RWMutex)、等待组(WaitGroup)、一次性锁(Once)和条件变量(Cond)。

  • 互斥锁(mutex)是一个基础的锁,它只能被一个goroutine同时持有。如果另一个goroutine试图获取一个已被持有的互斥锁,它将被阻塞,直到持有锁的goroutine释放锁。
package main

import (
    "fmt"
    "sync"
)

func main() {
    // 创建一个互斥锁
    var mutex sync.Mutex

    // 使用互斥锁保护共享资源
    mutex.Lock()
    fmt.Println("Only one goroutine can access the shared resource at a time.")
    mutex.Unlock()
}
  • 读写锁(RWmutex)是一种更高级的锁,它允许多个goroutine同时读取受保护的数据,但只允许一个goroutine同时写入受保护的数据。这可以提高程序的性能,因为读取操作通常比写入操作要快。
package main

import (
    "fmt"
    "sync"
)

func main() {
    // 创建一个读写锁
    var rwmutex sync.RWMutex

    // 使用读写锁保护共享资源
    rwmutex.RLock()
    fmt.Println("Multiple goroutines can read the shared resource at the same time.")
    rwmutex.RUnlock()

    // 使用写锁保护共享资源
    rwmutex.Lock()
    fmt.Println("Only one goroutine can write to the shared resource at a time.")
    rwmutex.Unlock()
}
  • 等待组(WaitGroup)是一个同步原语,它允许一个goroutine等待其他一组goroutine完成。
package main

import (
    "sync"
    "fmt"
)

var wg sync.WaitGroup

func main() {
    // 创建10个goroutine来并发地执行任务
    for i := 0; i < 10; i++ {
        wg.Add(1) // 告诉等待组有1个goroutine需要等待
        go func() {
            // 执行任务
            fmt.Println("Hello, world!")
            wg.Done() // 告诉等待组当前goroutine已完成
        }()
    }

    // 等待所有goroutine完成
    wg.Wait()

    fmt.Println("All goroutines have finished.")
}
  • 一次性锁(Once)是一个同步原语,它确保某个操作只被执行一次。
package main

import (
    "sync"
    "fmt"
)

var once sync.Once

func main() {
    // 只执行一次init函数
    once.Do(func() {
        fmt.Println("Hello, world!")
    })
}
  • 条件变量(Cond)是一种同步原语,它允许一个goroutine等待某个条件满足。
package main

import (
    "sync"
    "fmt"
    "time"
)

var cond = sync.NewCond(&sync.Mutex{})
var count int

func main() {
    go func() {
        // 等待count大于0
        cond.L.Lock()
        for count <= 0 {
            cond.Wait()
        }
        cond.L.Unlock()

        fmt.Println("Count is greater than 0.")
    }()

    // 将count设置为1,并通知等待的goroutine
    time.Sleep(1 * time.Second)
    cond.L.Lock()
    count = 1
    cond.Signal()
    cond.L.Unlock()
}

3.锁的注意事项

  • 死锁:死锁是指两个或多个goroutine相互等待对方释放锁,从而导致程序永远无法继续执行。为了避免死锁,必须确保每个goroutine在释放一个锁之前都必须获取该锁。
  • 锁争用:锁争用是指多个goroutine同时尝试获取同一个锁,从而导致程序性能下降。为了减少锁争用,可以尽量使用读写锁,并减少锁的持有时间。
  • 锁粒度:锁粒度是指锁保护的资源的范围。锁粒度越小,对程序并发性的影响就越小。因此,在选择锁的类型和粒度时,应权衡锁的性能和并发性。

?

文章来源:https://blog.csdn.net/u013533380/article/details/135545584
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。