Golang中互斥锁和读写互斥锁

简介: Golang中互斥锁和读写互斥锁

互斥锁


在Golang中,互斥锁(Mutex)是一种基本的同步原语,用于实现对共享资源的互斥访问。互斥锁通过在代码中标记临界区来控制对共享资源的访问,从而保证同一时间只有一个 goroutine 可以访问共享资源,避免了并发访问时的数据竞争和不一致性问题。


       互斥锁的主要方法包括两个,分别是 Lock 和 Unlock。Lock 方法用于锁定共享资源,防止其他 goroutine 访问;Unlock 方法则用于解锁共享资源,允许其他 goroutine 访问。一般来说,在使用互斥锁时,需要先通过 Lock 方法锁定共享资源,访问共享资源,然后再通过 Unlock 方法解锁共享资源,让其他 goroutine 可以访问。


使用互斥锁的示例代码

package main
import (
    "fmt"
    "sync"
)
var count int
var mutex sync.Mutex
func increment() {
  mutex.Lock()
  count++
  mutex.Unlock()
  wg.Done()
}
func main() {
    for i := 0; i < 1000; i++ {
    wg.Add(1)
    go increment()
  }
  wg.Wait()
  fmt.Println("Final count:", count)
}


在上面的示例代码中,increment函数是一个goroutine,它用来对count变量进行加1操作。在函数执行前通过mutex.Lock()获取互斥锁,在函数执行结束后通过mutex.Unlock()释放互斥锁。这样就保证了同一时刻只有一个goroutine可以访问count变量,从而避免了数据竞争的问题。


需要注意的是,在使用互斥锁时,一定要注意加锁和解锁的位置,否则可能会出现死锁的问题。


读写互斥锁

     

Go语言中的读写互斥锁(RWMutex)是一种特殊类型的互斥锁,它允许多个协程同时读取某个共享资源,但在写入时必须互斥,只能有一个协程进行写操作。相比互斥锁,读写互斥锁在高并发读的场景下可以提高并发性能,但在高并发写的场景下仍然存在性能瓶颈。


       读写互斥锁有两个方法:RLock()和RUnlock()。在读取共享资源时,可以调用RLock()方法加读锁,在读取完成后,需要调用RUnlock()方法释放读锁。在写入共享资源时,需要调用Lock()方法加写锁,在写入完成后,需要调用Unlock()方法释放写锁。当有写锁或读写锁时,不能再加读锁或写锁,直到已经释放了所有锁。


读写互斥锁的示例代码


package main
import (
    "fmt"
    "sync"
    "time"
)
var (
    value    int
    rwLock   sync.RWMutex
    waitTime time.Duration = 100 * time.Millisecond
)
func readValue() {
    rwLock.RLock()
    defer rwLock.RUnlock()
    time.Sleep(waitTime)
    fmt.Println("Read value:", value)
}
func writeValue(val int) {
    rwLock.Lock()
    defer rwLock.Unlock()
    time.Sleep(waitTime)
    value = val
    fmt.Println("Write value:", value)
}
func main() {
    // 读操作可以并行执行
    for i := 0; i < 5; i++ {
        go readValue()
    }
    // 写操作必须等待读操作全部结束后才能执行
    for i := 0; i < 5; i++ {
        go writeValue(i)
    }
    // 等待所有goroutine执行完毕
    time.Sleep(time.Second)
}

在这个示例中,使用了一个全局变量value来存储值,使用了一个sync.RWMutex类型的变量rwLock来保护这个变量的读写。在readValue函数中,首先调用RLock方法获取读锁,然后等待一段时间,最后输出变量value的值。在writeValue函数中,首先调用Lock方法获取写锁,然后等待一段时间,将传入的值赋给变量value,最后输出变量value的值。


在main函数中,首先启动5个goroutine来执行readValue函数,这些goroutine可以并行执行。然后启动5个goroutine来执行writeValue函数,这些goroutine必须等待所有的读操作完成后才能执行,因为它们需要获取写锁。


需要注意的是,在使用读写互斥锁时,必须保证写操作只有一个,否则就会出现竞争状态,导致数据不一致的问题。同时也需要注意使用锁的力度,避免锁的范围过大,导致性能下降。

相关文章
|
7月前
|
安全 Go
Golang深入浅出之-互斥锁(sync.Mutex)与读写锁(sync.RWMutex)
【4月更文挑战第23天】Go语言并发编程中,`sync.Mutex`和`sync.RWMutex`是保证线程安全的关键。互斥锁确保单个goroutine访问资源,而读写锁允许多个读者并发访问。常见问题包括忘记解锁、重复解锁以及混淆锁类型。使用`defer`可确保解锁,读写锁不支持直接升级或降级,需释放后再获取。根据读写模式选择合适锁以避免性能下降和竞态条件。理解并正确使用锁是编写并发安全程序的基础。
135 3
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
89 4
|
4月前
|
SQL 安全 Java
golang为什么不支持可重入锁?
本文对比分析了Java与Go语言中锁机制的不同。在Java中,无论是`synchronized`关键字还是`ReentrantLock`都支持可重入特性,通过维护一个计数器来跟踪锁的嵌套级别,确保同一线程可以多次获取同一把锁而不会造成死锁。然而,Go语言的`sync.Mutex`并不支持这一特性,其设计理念认为可重入锁往往指向代码设计问题,鼓励开发者重构代码以避免此类需求。文章进一步解释了这种设计理念背后的原因,并提供了替代方案示例。总体而言,Go语言试图从设计层面避免潜在的代码问题,尽管这可能会增加一定的开发复杂性。
golang为什么不支持可重入锁?
|
7月前
|
安全 Go
Golang深入浅出之-Go语言标准库中的文件读写:io/ioutil包
【4月更文挑战第27天】Go语言的`io/ioutil`包提供简单文件读写,适合小文件操作。本文聚焦`ReadFile`和`WriteFile`函数,讨论错误处理、文件权限、大文件处理和编码问题。避免错误的关键在于检查错误、设置合适权限、采用流式读写及处理编码。遵循这些最佳实践能提升代码稳定性。
78 0
|
7月前
|
Go
Golang 中的互斥锁是什么?
# go # programming # beginners # architecture
|
7月前
|
存储 安全 Go
Golang分段锁
Golang分段锁
97 0
|
7月前
|
存储 Go
浅谈Golang互斥锁sync.Mutex
浅谈Golang互斥锁sync.Mutex
55 0
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
121 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
68 4
Golang语言文件操作快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
106 3
Golang语言之gRPC程序设计示例