c语言编程笔录

首页 >   > 笔记大全

笔记大全

golang并发锁如何使用

更新时间:2023-11-10

Go语言中的并发锁

并发锁是Go语言中实现并发安全的一种机制,可以保证不同协程之间的临界区代码不被同时执行。Go语言中提供了多种锁机制,比如:互斥锁、读写锁、条件变量。

互斥锁 Mutex

互斥锁Mutex是Go语言中最基础的并发锁机制,可以在代码块前后使用Mutex的Lock()和Unlock()方法实现锁的获取和释放。

package main

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

type Counter struct {
    sum int
    mux sync.Mutex
}

func (c *Counter) Add(i int) {
    c.mux.Lock()
    defer c.mux.Unlock()
    time.Sleep(100 * time.Millisecond)
    c.sum += i
}

func (c *Counter) Value() int {
    c.mux.Lock()
    defer c.mux.Unlock()
    return c.sum
}

func main() {
    var wg sync.WaitGroup
    var counter Counter
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            counter.Add(1)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println(counter.Value())
}

在上面的代码中,我们定义了一个Counter类型,它有一个内部字段sum,以及一个互斥锁mux。Add方法和Value方法都使用了锁,以保证它们对于字段sum的访问是安全的。

读写锁 RWMutex

读写锁是一种比互斥锁更高效的锁机制,它允许多个协程同时读取某一个共享资源,但是在写的时候必须互斥访问,保证写的安全性。

package main

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

type Counter struct {
    sum int
    mux sync.RWMutex
}

func (c *Counter) Add(i int) {
    c.mux.Lock()
    defer c.mux.Unlock()
    time.Sleep(100 * time.Millisecond)
    c.sum += i
}

func (c *Counter) Value() int {
    c.mux.RLock()
    defer c.mux.RUnlock()
    return c.sum
}

func main() {
    var wg sync.WaitGroup
    var counter Counter
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            counter.Add(1)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println(counter.Value())
}

在上面的代码中,我们将Mutex替换成了RWMutex,Add方法仍然使用互斥锁来保证写的安全性,而Value方法则使用读锁来允许多协程之间的并发读取。

条件变量 Cond

条件变量是一种在协程之间等待和通知的机制,它可以让协程在满足特定条件之前等待,或者在满足特定条件时通知其他协程。

package main

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

type Counter struct {
    sum int
    mux sync.Mutex
    cond *sync.Cond
}

func NewCounter() *Counter {
    c := &Counter{}
    c.cond = sync.NewCond(&c.mux)
    return c
}

func (c *Counter) Add(i int) {
    c.mux.Lock()
    defer c.mux.Unlock()
    time.Sleep(100 * time.Millisecond)
    c.sum += i
    c.cond.Broadcast()
}

func (c *Counter) Wait(n int) {
    c.mux.Lock()
    defer c.mux.Unlock()
    for c.sum < n {
        c.cond.Wait()
    }
}

func main() {
    var wg sync.WaitGroup
    var counter = NewCounter()
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            counter.Add(1)
            wg.Done()
        }()
    }
    wg.Wait()
    counter.Wait(1000)
    fmt.Println(counter.Value())
}

在上面的代码中,我们使用了一个条件变量来实现等待某一个特定条件。Wait方法会在条件变量发生变化时被唤醒,并返回。

总结

在Go语言中,我们可以通过互斥锁、读写锁和条件变量等不同的锁机制来实现并发安全。我们应该根据具体的场景来选择适合的锁机制。

互斥锁是Go语言中最基础的锁机制,其安全性和性能表现都比较稳定。但是当场景中存在大量的读操作时,互斥锁对性能的影响将会相对较大,而读写锁则可以在不降低写性能的前提下提高并发度。

条件变量则是一种轻量级的等待和通知机制,它可以让协程更高效地等待某些必要条件的发生,并通知其他协程执行特定的操作。

总之,在选择锁机制时应该根据具体的场景来选择合适的锁,以保证代码的性能、安全和可维护性。