Golang并发锁使用详解:sync.Mutex与RWMutex实战

唐可
唐可
发布时间:2026-10-15| 5人浏览过
原创

本文通过具体的众筹存钱案例,揭示Golang多协程并发访问共享数据导致的结果错误,深入解析如何使用sync.Mutex解决基本线程安全问题,并利用sync.RWMutex优化读多写少场景的性能。

当多个Goroutine同时访问和修改同一个变量时,若不进行同步处理,极易引发数据竞争,导致结果不符合预期。以多人向银行账户并发存入资金为例,如果直接运行以下代码,最终余额往往小于实际存入总额。
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func Save(income int, balance *int) {
	defer wg.Done()
	*balance = *balance + income
}

func main() {
	balance := 0

	for s := 0; s < 1000; s++ {
		wg.Add(1)
		go Save(1, &balance)
	}

	wg.Wait()
	fmt.Println("当前账户余额:", balance)
}
由于Goroutine的调度顺序不确定,多个协程可能同时读取到相同的余额值(例如0),计算后依次回写。这会导致部分存款被覆盖,最终输出结果通常为随机值,而非预期的1000元。要解决此问题,必须引入并发锁机制,确保同一时刻仅有一个协程能修改该数据。

使用互斥锁保证数据安全

Go标准库中的 sync.Mutex 是解决此类问题的基础工具。通过在修改变量前调用 Lock(),修改完成后调用 Unlock(),可以确保操作的原子性。
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup
var lk sync.Mutex

func Save(income int, balance *int) {
	defer wg.Done()
	lk.Lock()
	*balance = *balance + income
	lk.Unlock()
}

func main() {
	balance := 0

	for s := 0; s < 1000; s++ {
		wg.Add(1)
		go Save(1, &balance)
	}

	wg.Wait()
	fmt.Println("当前账户余额:", balance)
}
加入锁逻辑后,无论协程如何调度,每次执行 *balance = *balance + income 操作时,其他尝试进入的协程都会被阻塞。多次运行测试,结果均稳定为“当前账户余额: 1000”,证明了数据的一致性得到了保障。

高并发读场景下的读写锁优化

互斥锁虽然简单有效,但属于排他锁。即使协程仅读取数据而不修改,也必须等待锁释放,这在“读多写少”的业务场景中会造成严重的性能瓶颈。为此,可以使用 sync.RWMutex(读写锁)进行优化。 读写锁的工作原理如下: - **写锁**:Lock() 会阻塞所有其他的读写协程,确保写入期间没有其他并发操作。 - **读锁**:RLock() 允许其他协程继续并发读取,只有在进行写操作时才需要独占。 以下为读写锁的应用演示:
package main

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

var wg sync.WaitGroup
var lk sync.RWMutex

// 模拟写操作
func Save(thr int, income int, balance *int) {
	defer wg.Done()
	lk.Lock()
	fmt.Printf("write-groutine-< %d >添加写锁\n", thr)
	*balance = *balance + income
	time.Sleep(time.Millisecond * 1)
	fmt.Printf("write-groutine-< %d >解除写锁\n", thr)
	lk.Unlock()
}

// 模拟读操作
func Show(thr int, balance *int) {
	defer wg.Done()
	lk.RLock()
	fmt.Printf("read-groutine-< %d >开始读取数据\n", thr)
	time.Sleep(time.Millisecond * 1)
	fmt.Printf("read-groutine-< %d >完成读取数据\n", thr)
	lk.RUnlock()
}

func main() {
	balance := 0
	StartTime := time.Now()

	// 3次写操作
	for s := 0; s < 3; s++ {
		wg.Add(1)
		go Save(s, 1, &balance)
	}

	// 10次读操作
	for sh := 0; sh < 10; sh++ {
		wg.Add(1)
		go Show(sh, &balance)
	}

	wg.Wait()
	fmt.Println("最终账户余额:", balance)
	TimeRange := time.Since(StartTime)
	fmt.Println("程序运行耗时: ", TimeRange)
}
运行结果可以观察到明显的差异: 1. 写操作严格按照顺序串行执行(一次完整的加锁、修改、解锁过程)。在写锁持有期间,后续的所有协程都被挂起。 2. 读操作几乎瞬间启动且乱序打印。只要没有协程占用写锁,所有的读取协程都可以并行获取读锁,从而极大地提升了程序的吞吐量。 以上就是Golang并发锁机制与sync.RWMutex应用的详细内容,更多关于Golang并发编程实战的资料请关注本站其它相关文章!
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。