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

 更新时间:2022年09月02日 09:09:52   作者:Sundar84034  
这篇文章主要为大家介绍了Go语言开发保证并发安全实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

什么是并发安全?

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

这里举一个简单的例子:

 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))
}

以上就是Go语言开发保证并发安全实例详解的详细内容,更多关于Go保证并发安全的资料请关注脚本之家其它相关文章!

相关文章

  • Go interface接口声明实现及作用详解

    Go interface接口声明实现及作用详解

    这篇文章主要为大家介绍了Go interface接口声明实现及作用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • 实现像php一样方便的go ORM数据库操作示例详解

    实现像php一样方便的go ORM数据库操作示例详解

    这篇文章主要为大家介绍了实现像php一样方便的go ORM数据库操作示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • go责任链行为型设计模式Chain Of Responsibility

    go责任链行为型设计模式Chain Of Responsibility

    这篇文章主要为大家介绍了go行为型设计模式之责任链Chain Of Responsibility使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 基于HLS创建Golang视频流服务器的优缺点

    基于HLS创建Golang视频流服务器的优缺点

    HLS 是 HTTP Live Streaming 的缩写,是苹果开发的一种基于 HTTP 的自适应比特率流媒体传输协议。这篇文章主要介绍了基于 HLS 创建 Golang 视频流服务器,需要的朋友可以参考下
    2021-08-08
  • 浅谈Go Channel 高级实践

    浅谈Go Channel 高级实践

    这篇文章主要介绍了浅谈Go Channel 高级实践,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • golang操作elasticsearch的实现

    golang操作elasticsearch的实现

    这篇文章主要介绍了golang操作elasticsearch,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • golang封装一个执行命令行的函数(return stderr/stdout/exitcode)示例代码

    golang封装一个执行命令行的函数(return stderr/stdout/exitcode)示例代码

    在 Go 语言中,您可以使用 os/exec 包来执行外部命令,不通过调用 shell,并且能够获得进程的退出码、标准输出和标准错误输出,下面给大家分享golang封装一个执行命令行的函数(return stderr/stdout/exitcode)的方法,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • golang整合日志zap的实现示例

    golang整合日志zap的实现示例

    Go语言中的zap库提供了强大的日志管理功能,支持日志记录到文件、日志切割、多日志级别、结构化日志输出等,它通过三种方法zap.NewProduction()、zap.NewDevelopment()和zap.NewExample(),快速构建适用于不同环境的logger,感兴趣的可以了解一下
    2024-10-10
  • Golang单元测试与断言编写流程详解

    Golang单元测试与断言编写流程详解

    这篇文章主要介绍了Golang单元测试与断言编写流程,单元测试也是一个很重要的事情。单元测试是指在开发中,对一个函数或模块的测试。其强调的是对单元进行测试
    2022-12-12
  • 浅析Go语言中的map数据结构是如何实现的

    浅析Go语言中的map数据结构是如何实现的

    在 Go 中,map 是一种用于存储键值对的数据结构,它提供了一种快速查找和访问数据的方式,下面我们就来看看Go语言中是如何实现map数据结构的吧
    2024-03-03

最新评论