锁Mutex

本文介绍了Golang中用于并发控制的两种锁:sync.Mutex和sync.RWMutex。sync.Mutex是互斥锁,确保同一时间只有一个goroutine访问资源;sync.RWMutex是读写锁,允许多个goroutine并行读取,但写入时互斥。文章详细讲解了两种锁的使用方法,包括加锁、解锁规则以及适用场景,并给出了示例代码。
摘要由CSDN通过智能技术生成

在 Golang 的并发编程中,锁是一种常用的同步机制,用于确保同一时间只有一个 goroutine 访问共享资源。Golang 中提供了两种锁:sync.Mutex 和 sync.RWMutex。

sync.Mutex 是一种互斥锁,只允许一个 goroutine 获得锁。当一个 goroutine 获得锁后,其他 goroutine 必须等待该 goroutine 释放锁后才能继续执行。sync.RWMutex 是一种读写锁,它允许多个 goroutine 同时读取共享资源,但是只允许一个 goroutine 写入共享资源。当一个 goroutine 拥有写入锁时,其他 goroutine 无法获得读取或写入锁,必须等待该 goroutine 释放锁后才能继续执行。

下面我们详细讲解 Golang 中的锁的使用。

使用 sync.Mutex

sync.Mutex 是一种互斥锁,可以使用 Lock 和 Unlock 方法进行加锁和解锁。

创建一个互斥锁对象

var mutex sync.Mutex

加锁操作

mutex.Lock()
defer mutex.Unlock()

解锁操作

mutex.Unlock()

在加锁的代码块中进行共享资源的操作,操作完成后释放锁。

注意点:

  1. 不要在已经加锁的代码块中再次加锁,否则会导致死锁。

  1. 加锁和解锁操作需要成对出现,否则会导致锁泄漏或者解锁已经解锁的锁。

  1. 互斥锁的使用会带来一定的性能损失,因为锁的获取和释放需要进行系统调用。

互斥锁的使用场景:

互斥锁适用于对共享资源的读写操作进行串行化的场景,例如:

对共享变量进行加减操作
var count intvar mutex sync.Mutex

func increment() {
    mutex.Lock()
    count++
    mutex.Unlock()
}
对共享资源进行读写的场景
var data map[string]intvar mutex sync.Mutex

func readData(key string)int {
    mutex.Lock()
    defer mutex.Unlock()
    return data[key]
}

func writeData(key string, value int) {
    mutex.Lock()
    defer mutex.Unlock()
    data[key] = value
}

在使用互斥锁时需要注意以下几点:

  1. 不要将互斥锁作为结构体的成员变量,因为这样会导致锁的拷贝问题。

  1. 不要在锁的代码块中进行耗时的操作,否则会影响程序的并发性能。

  1. 对于一些读多写少的场景,可以考虑使用读写锁(RWMutex)来替代互斥锁,以提高程序的并发性能。

  1. 在使用锁的代码中,尽量避免使用 panic,因为 panic 会破坏锁的语义,导致锁不能正确释放

下面是一个使用互斥锁的案例,

package main

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

var count intvar lock sync.Mutex

fun cincrement() {
    lock.Lock()
    defer lock.Unlock()
    count++
}

func main() {
    for i := 0; i < 10; i++ {
        gofunc() {
            for j := 0; j < 100; j++ {
                increment()
            }
        }()
    }
    time.Sleep(time.Second)
    fmt.Println("count:", count)
}

在上面的代码中,我们定义了一个全局变量 count 和一个 Mutex 锁 lock。increment 函数用于增加 count 的值,在该函数中首先调用 lock.Lock() 方法进行加锁,然后使用 defer 关键字在函数返回时调用 lock.Unlock() 方法进行解锁。这样可以确保在任何情况下,加锁和解锁都是成对出现的。

在 main 函数中,我们启动了 10 个 goroutine,每个 goroutine 都会执行 100 次 increment 函数。最后输出 count 的值,由于我们使用了锁来保护 count 的修改,因此最终的输出结果一定是 1000。

使用 sync.RWMutex

在并发编程中,读写锁是一种用于读写分离的机制。在读取数据的时候,可以允许多个线程同时读取数据,但是在写入数据的时候,必须互斥,只能有一个线程进行写操作。

Golang标准库中提供了读写锁的实现,包含两个结构体:

  • sync.RWMutex:读写锁结构体,包含读锁和写锁;

  • sync.RLocker:只读锁接口,该接口包含一个只读锁方法RLock()。

在使用读写锁的时候,需要注意以下几点:

  1. 对读写锁进行锁定的方法必须是相同的锁类型,不能同时使用读锁和写锁进行锁定,否则会导致死锁。

  1. 在读写锁中,读锁是共享的,多个读锁可以同时获得,而写锁是互斥的,只能有一个写锁被获得。因此,在读写锁中,读操作和写操作是不同的。读写锁适用于读多写少的情况。

  1. 读写锁不能保证写锁等待的公平性,当一个写锁在等待时,新的读锁可以获得,这可能会导致写锁等待的时间较长。如果需要保证写锁等待的公平性,可以考虑使用互斥锁。

使用读写锁的场景:

读多写少的场景,可以使用读写锁来实现

sync.RWMutex 是一种读写锁,可以使用 RLock 和 RUnlock 方法进行读取锁的加锁和解锁,使用 Lock 和 Unlock 方法进行写入锁的加锁和解锁。

package main

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

var count intvar rwlock sync.RWMutex

func increment() {
    rwlock.Lock()
    defer rwlock.Unlock()
    count++
}

func read() {
    rwlock.RLock()
    defer rwlock.RUnlock()
    fmt.Println("count:", count)
}

func main() {
    for i := 0; i < 10; i++ {
        gofunc() {
            for j := 0; j < 10; j++ {
                increment()
                read()
            }
        }()
    }
    time.Sleep(time.Second)
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值