golang中的并发安全和锁
2024-09-03 04:58:53
1. 并发安全
package main import (
"fmt"
"sync"
) var (
sum int
wg sync.WaitGroup
) func test() {
for i := 0; i < 5000000; i++ {
sum += 1
}
wg.Done()
} func main() {
// 并发和安全锁
wg.Add(2)
go test()
go test() wg.Wait()
fmt.Println(sum) }
上面的代码中我们开启了两个goroutine去累加变量x的值,这两个goroutine在访问和修改x变量的时候就会存在数据竞争,导致最后的结果与期待的不符。
2. 互斥锁
package main import (
"fmt"
"sync"
) var (
sum int
wg sync.WaitGroup
mu sync.Mutex // 定义一个互斥锁
) func test() {
for i := 0; i < 10000000; i++ {
// 互斥锁它能够保证同时只能有一个goroutine去访问共享资源
mu.Lock()
sum += 1
mu.Unlock()
}
wg.Done()
} func main() {
fmt.Println(mu)
// 并发和安全锁
wg.Add(2)
go test()
go test() wg.Wait()
fmt.Println(sum) }
使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。
3. 读写互斥锁
互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。
读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。
package main import (
"fmt"
"sync"
"time"
) var (
x int
wg sync.WaitGroup
mu sync.Mutex // 定义一个互斥锁
rw sync.RWMutex // 定义一个读写锁,注意:只有读多写少的时候,读写锁才能发挥其优势
) func write() {
rw.Lock()
x += 1
time.Sleep(10 * time.Millisecond) // 假设写入时间耗费10毫秒
rw.Unlock()
wg.Done()
}
func read() {
rw.RLock()
time.Sleep(time.Millisecond)
rw.RUnlock()
wg.Done()
} func main() {
start := time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
} // 写耗时:160毫秒左右 for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
} // 读耗时:15毫秒左右 wg.Wait()
end := time.Now()
fmt.Println("执行时间:", end.Sub(start))
}
需要注意的是读写锁非常适合读多写少的场景,如果读和写的操作差别不大,读写锁的优势就发挥不出来。
最新文章
- php调用阿里大鱼 接口curl
- thinkphp 3.2.3 连接sql server 2014 WAMPSERVER环境包
- 高通APQ8074 spi 接口配置
- 国外HTML网站模版(卖成品模版)
- win7中protel99添加元件库
- 2D游戏编程4&mdash;Windows事件
- 给定一个字符串里面只有";R"; ";G"; ";B"; 三个字符,请排序,最终结果的顺序是R在前 G中 B在后。 要求:空间复杂度是O(1),且只能遍历一次字符串。
- linux No space left on device 由索引节点(inode)爆满引发500问题
- 从淘宝和网易的font-size思考移动端怎样使用rem?
- 游戏服务器之Java热更新
- 「loj3058」「hnoi2019」白兔之舞
- 快速学习HTML
- 如何进行SQL排序
- 2019.01.04 洛谷P4719 【模板】动态dp(链分治+ddp)
- C#实时检测端口占用情况
- unity3d内存管理坑爹之处
- 缓慢变化维 (Slowly Changing Dimension) 常见的三种类型及原型设计(转)
- 视图层view layer
- UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数
- HP电脑的增霸卡功能操作详解
热门文章
- IDEA结合mybatis插件自动生成代码
- Python 图片转字符画 学习笔记
- 【LeetCode】829. Consecutive Numbers Sum 解题报告(C++)
- 【LeetCode】67. Add Binary 解题报告(Python)
- 【LeetCode】693. Binary Number with Alternating Bits 解题报告(Python)
- 【LeetCode】238. Product of Array Except Self 解题报告(Python & C++)
- python语法糖之有参装饰器、无参装饰器
- 知识增强的预训练语言模型系列之ERNIE:如何为预训练语言模型注入知识
- Android物联网应用程序开发(智慧城市)—— 购物信息的存储界面开发
- linux rm 删除命令