【Go】并发安全读写锁、互斥锁的应用

目录

1、示例

2、独占锁/互斥锁(Mutex Lock)

3、读写锁(RWMutex)


1、示例

有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态)。类比现实生活中的例子有十字路口被各个方向的的汽车竞争;还有火车上的卫生间被车厢里的人竞争。

package main

import (
	"fmt"
	"sync"
)

var x int64
var wg sync.WaitGroup

func add() {
	for i := 0; i < 400000; i++ {
		x = x + 1
	}
	wg.Done()
}
func main() {
	wg.Add(2)
	go add()
	go add()
	wg.Wait()
	fmt.Println(x)
}

 上面的代码中我们开启了两个goroutine去累加变量x的值,这两个goroutine在访问和修改x变量的时候就会存在数据竞争,导致最后的结果与期待的不符。

2、独占锁/互斥锁(Mutex Lock)

在并发编程中,"并发安全"是指程序在多个并发执行的线程或协程中,仍然能够正确地执行并产生正确结果的特性。并发安全是一个重要的概念,因为并发程序可能会面临多个线程同时访问共享资源的情况,这可能导致数据竞争和不确定的行为。为了确保并发程序的正确性,我们需要采取适当的措施来处理并发安全问题。

其中一个常用的处理并发安全问题的方法是使用锁机制。锁(Lock)是一种同步工具,用于保护共享资源,以便在同一时间只有一个线程可以访问该资源。锁可分为独占锁和共享锁。

  • 独占锁/互斥锁(Mutex Lock):独占锁是最常用的锁类型,也称为互斥锁(Mutex Lock),在同一时间只允许一个线程获取锁,其他线程必须等待锁的释放。通过互斥锁,可以确保同一时间只有一个线程访问被保护的代码块或共享资源。
package main

import (
	"fmt"
	"sync"
)

var x int64
var wg sync.WaitGroup
var mutex sync.Mutex

func add() {
	for i := 0; i < 400000; i++ {
		mutex.Lock()
		x = x + 1
		mutex.Unlock()
	}
	wg.Done()
}
func main() {
	wg.Add(2)
	go add()
	go add()
	wg.Wait()
	fmt.Println(x)
}

 在上述示例中,sync.Mutex是Go语言内置的互斥锁类型。通过调用Lock方法获取锁,在锁保护的代码块中进行共享资源的访问或修改,最后调用Unlock方法释放锁。

3、读写锁(RWMutex)

  • 读写锁(RWMutex):读写锁是一种特殊的锁,它允许多个线程同时读取共享资源,但只允许一个线程进行写操作。读写锁在读多写少的场景中可以提供更高的并发性能。
package main

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

var (
	x      int64
	wg     sync.WaitGroup
	rwlock sync.RWMutex
)

func write() {
	// lock.Lock()   // 加互斥锁
	rwlock.Lock() // 加写锁
	x = x + 1
	time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
	rwlock.Unlock()                   // 解写锁
	// lock.Unlock()                     // 解互斥锁
	wg.Done()
}

func read() {
	// lock.Lock()                  // 加互斥锁
	rwlock.RLock()               // 加读锁
	time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
	rwlock.RUnlock()             // 解读锁
	// lock.Unlock()                // 解互斥锁
	wg.Done()
}

func main() {
	start := time.Now()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go write()
	}

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go read()
	}

	wg.Wait()
	end := time.Now()
	fmt.Println(end.Sub(start))
}

使用锁机制可以有效地解决并发安全问题,确保共享资源的正确访问和修改。然而,过度使用锁也可能导致性能下降和死锁等问题,因此在使用锁时需要谨慎,并根据具体应用场景选择适当的锁类型和策略。

猜你喜欢

转载自blog.csdn.net/fanjufei123456/article/details/131249121