Go语言开发保证并发安全实例详解

作者:Sundar84034 时间:2024-02-21 10:19:48 

什么是并发安全?

高并发场景下,进程、线程(协程)可能会发生资源竞争,导致数据脏读、脏写、死锁等问题,为了避免此类问题的发生,就有了并发安全。

这里举一个简单的例子:

var data int
go func() {
  data++
}()
if data == 0 {
  fmt.Printf("the value is %v.\n", data)
}

在这段代码中

第2行go关键字开启了一个新的协程,来执行data++操作

第5行,对data变量进行了读取判断的操作

以上两部是由2个不同线程/协程运行,且没有任何措施保证执行顺序,所以执行结果是不确定的。

  • 没有输出。(第3行是在第5行之前执行的)

  • 输出 the value is 0。(第5行和第6行在第3行之前执行)

  • 输出 the value is 1。(第5行在第3行之前执行,但第3行在第6行之前执行)

Go如何保证并发安全

目前了解到的,大概有这3种,Mutex、Channel、Atomic

Mutex

加锁应该是最常见的并发控制方法,一般分成两种,乐观锁和悲观锁

锁是由操作系统的调度器来实现的,锁通常用来保护一段逻辑,

悲观锁

悲观锁是一种悲观思想,它总认为最坏的情况可能会出现。不管意料之外的结果是否会发生,只要存在发生的可能,就在操作这个资源之前先上锁。例如互斥锁读写锁都是悲观锁。

在go中,除了automic,其它都是悲观锁

悲观锁应该都是由操作系统的调度器来实现的,通常用来保护一段逻辑,主要是通过阻塞其它线程,保证当前时刻只有一个线程在对资源进行操作,因此性能相对较差,浪费了计算机多核的优势。

乐观锁

乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过

乐观锁的实现方案主要包含CAS版本号机制

乐观锁适用于多读的场景,可以提高吞吐量。

版本号机制

通过在数据表中,增加一个版本号字段,当数据发生更新时,版本号值发生改变。 例如一个线程A想要更新变量s的值,在读取s的值的同时读取版本号,在提交更新时,用之前读到的版本号值与当前的版本号值进行比对,当且仅当版本号值一致时,才会触发更新,否则不断进行重试,直到更新成功。

CAS

CAS全名为Compare And Swap,即比较与转换,是一种有名的无锁算法。在不使用锁的情况下,实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步,

互斥锁

GO使用Sync包的Mutex类型来实现互斥锁,它能保证同时只有一个goroutine可以访问资源。

func sample() {
var l sync.Mutex
l.Lock()
       defer l.Unlock()
// so something
}

读写互斥锁

GO使用Sync包的RWMutex类型来实现互斥锁。当我们去并发的读取一个资源,只要数据没有发生写入,是没必要加锁的。因此读多写少的情况下,使用读写互斥锁是更好的选择,性能更好。

读写锁分为两种:读锁和写锁。

当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁可以顺利获得,如果是获取写锁就会等待;

当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。

package main
import (
"fmt"
"sync"
"time"
)
var (
wg     sync.WaitGroup
rwlock sync.RWMutex
)
func write() {
rwlock.Lock() // 加写锁
time.Sleep(10 * time.Millisecond)
rwlock.Unlock() // 解写锁
wg.Done()
}
func read() {
rwlock.RLock() // 加读锁
time.Sleep(time.Millisecond)
rwlock.RUnlock() // 解读锁
wg.Done()
}
func main() {
start := time.Now()
//读多
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
//写少
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start))
}

来源:https://juejin.cn/post/7025797208209358862

标签:Go,并发安全,并发
0
投稿

猜你喜欢

  • PHP设计模式之装饰器模式定义与用法详解

    2023-09-11 18:41:48
  • mysql 5.7.21 winx64安装配置方法图文教程

    2024-01-28 10:32:00
  • 用python打印菱形的实操方法和代码

    2023-03-18 19:17:18
  • 详解Python中的路径问题

    2021-06-05 08:48:45
  • 利用Python校准本地时间的方法教程

    2021-06-24 06:51:41
  • linux实现mysql数据库每天自动备份定时备份

    2024-01-20 11:58:53
  • python操作xml文件示例

    2022-06-17 22:38:28
  • mysql 8.0.16 winx64及Linux修改root用户密码 的方法

    2024-01-29 00:35:54
  • Python 基于FIR实现Hilbert滤波器求信号包络详解

    2023-07-13 01:31:47
  • JavaScript在ASP页面中实现掩码文本框效果代码

    2013-06-01 19:57:23
  • Django REST Swagger实现指定api参数

    2023-03-10 05:48:58
  • Vue 3.x+axios跨域方案的踩坑指南

    2024-05-09 09:21:15
  • tensorflow pb to tflite 精度下降详解

    2023-05-25 19:05:41
  • 关于ASP eof与bof 区别分析

    2011-03-11 11:14:00
  • 五个提升Python的执行效率的技巧分享

    2021-01-07 09:52:45
  • Go语言HTTPServer开发的六种方式小结

    2023-06-22 21:48:21
  • python memory_profiler库生成器和迭代器内存占用的时间分析

    2023-01-06 21:25:02
  • 启动iis出现发生意外0x8ffe2740的解决方法

    2011-03-31 11:19:00
  • JS实现非首屏图片延迟加载的示例

    2024-04-23 09:27:49
  • JS简单获取并修改input文本框内容的方法示例

    2024-05-10 14:07:59
  • asp之家 网络编程 m.aspxhome.com