GO中sync包自由控制并发示例详解


Posted in Golang onAugust 05, 2022

资源竞争

channel 常用于并发通信,要保证并发安全,主要使用互斥锁。在并发的过程中,当一个内存被多个 goroutine 同时访问时,就会产生资源竞争的情况。这块内存也可以称为共享资源。

并发时对于共享资源必然会出现抢占资源的情况,如果是对某资源的统计,很可能就会导致结果错误。为保证只有一个协程拿到资源并操作它,可以引入互斥锁 sync.Mutex。

sync.Mutex

互斥锁,指的是并发时,在同一时刻只有一个协程执行某段代码,其他协程只有等待该协程执行完成后才能继续执行。

var (sum int 
 mutex sync.Mutex)
func add(i int){
    mutex.Lock()
    sum+=i
    mute.Unlock()
}

使用 mutex 加锁保护 sum+ =i 的代码片段,这样这个片段区就无法同时被多个协程访问,当有协程进入该片段区,那其他的协程就只有等待,以此保证临界区的并发安全。

sync.Mutex 只有 Lock()和 Unlock() 方法,它们是成对存在的,且Lock后一定要执行Unlock进行释放锁。所以可以使用 defer 语句释放锁,以保证锁一定会被释放。

func add(i int){
    mutex.Lock()
    defer mutex.Unlock()
    sum += i
}

sync.RWMutex

上面例子是对 sum 写操作时使用sync.Mutex 保证并发安全,当多个协程进行读取操作时,避免因并发产生读取数据不正确,也是可以使用互斥锁 sync.Mutex。

func getSum(){
    mutex.Lock()
    defer mutex.Unlock()
    b:=sum
    return b
}

多个协程 goroutine 同时读写的资源竞争问题解决,还需要考虑性能问题,每次读写共享资源都加锁,也会导致性能低。

多个协程并发进行读写时会遇到以下情况:

  • 写时不能同时读,易读到脏数据
  • 读时不能同时写,因为会导致结果不一致
  • 读时同时写,因数据不变,无论多少个 goroutine 读都是并发安全

使用读写锁 sync.RWMutex 优化代码:

var mutex sync.RWMutex
func readSum() int {
    mutex.RLock()
    defer mutex.RUnlock()
    b := sum
    return b
}

读写锁的性能比互斥锁性能好。

sync.WaitGroup

为了能够监听所有的协程的执行,一旦所有的goroutine 都执行完成,程序应当及时退出节省时间提高性能。通过使用 sync.WaitGroup 来解决。使用步骤如下:

  • 声明一个 sync.WaitGroup ,通过 add 方法增加计数器值,有几个协程就计算几个
  • 每个协程结束后就调用 Done 方法,主要是让计数器减1
  • 最后调用 Wait 方法一直等待,直到计数器为 0 时,所有跟踪的协程都执行完毕
func run() {
    var wg sync.WaitGroup
    wg.Add(100)
    for i := 0; i < 100; i++ {
        go func() {
            defer wg.Done()
            add(10)
        }()
    }
    wg.Wait()
}

通过 sync.WaitGroup 可以很好地跟踪协程.

sync.Once

sync.Once 作用是让代码只执行一次。详细使用是调用方法 once.Do 方法,具体实现:

func main(){
    var once sync.once
    oneFunc := func(){
        println("once func")
    }
    once.Do(oneFunc)
}

sync.Once 适用于创建某个对象的单例、只加载一次的资源等只执行一次的场景。

sync.Cond

使用 sync.WaitGroup 主要是控制等待所有的协程都执行完毕,才最终完成。但是当遇到场景是,只有等待所有条件都准备好才开始。sync.Cond 相当于发号施令,只有通知执行所有的协程才可以执行,重点是所有协程需等待唤醒才可以开始。

所以 sync.Cond 具有阻塞协程和唤醒协程的功能。详细的用法:

  • 通过 sync.NewCond 函数生成一个 *sync.Cond,用于阻塞和唤醒协程
  • 调用 cond.Wait() 方法阻塞当前协程等待,需要注意调用 cond.Wait() 方法要加锁
  • 调用 cond.Broadcast() 后所有协程才开始执行
func run() {
    cond := sync.NewCond(&amp;sync.Mutex{})
    var wg sync.WaitGroup
    wg.Add(101)
    for i := 0; i &lt; 100; i++ {
        go func(num int) {
            defer wg.Done()
            fmt.Println(num, "号正在 awaiting......")
            cond.L.Lock()
            cond.Wait() //等待所有协程准备完成
            fmt.Println(num, "号开始跑……")
            cond.L.Unlock()
        }(i)
    }
    // 等待所有的协程都进入 wait 状态
    time.Sleep(2*time.Second)
    go func() {
        defer wg.Done()
        // 所有都准备完成,开始
        cond.Broadcast()
    }()
    // 防止函数提前返回退出
    wg.Wait()
}

以上就是GO中sync包自由控制并发示例详解的详细内容,更多关于GO sync包控制并发的资料请关注三水点靠木其它相关文章!

Golang 相关文章推荐
Go语言操作数据库及其常规操作的示例代码
Apr 21 Golang
go语言求任意类型切片的长度操作
Apr 26 Golang
彻底理解golang中什么是nil
Apr 29 Golang
golang DNS服务器的简单实现操作
Apr 30 Golang
golang gopm get -g -v 无法获取第三方库的解决方案
May 05 Golang
go语言基础 seek光标位置os包的使用
May 09 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
Golang二维数组的使用方式
May 28 Golang
Go语言空白表示符_的实例用法
Jul 04 Golang
浅谈GO中的Channel以及死锁的造成
Mar 18 Golang
Go语言grpc和protobuf
Apr 13 Golang
Go语言编译原理之源码调试
Aug 05 #Golang
Go语言编译原理之变量捕获
Aug 05 #Golang
在ubuntu下安装go开发环境的全过程
Aug 05 #Golang
Go语言测试库testify使用学习
Jul 23 #Golang
Go语言怎么使用变长参数函数
Jul 15 #Golang
Go微服务项目配置文件的定义和读取示例详解
Jun 21 #Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 #Golang
You might like
php中autoload的用法总结
2013/11/08 PHP
php7 安装yar 生成docker镜像
2017/05/09 PHP
PHP实现批量重命名某个文件夹下所有文件的方法
2017/09/04 PHP
Laravel5.1 框架Middleware中间件基本用法实例分析
2020/01/04 PHP
JS中剪贴板兼容性、判断复制成功或失败
2021/03/09 Javascript
FF IE兼容性的修改小结
2009/09/02 Javascript
javascript学习笔记(十一) 正则表达式介绍
2012/06/20 Javascript
js生成动态表格并为每个单元格添加单击事件的方法
2014/04/14 Javascript
JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承)
2014/08/16 Javascript
ztree获取选中节点时不能进入可视区域出现BUG如何解决
2015/12/03 Javascript
Node.js开发者必须了解的4个JS要点
2016/02/21 Javascript
jQuery插件开发汇总
2016/05/15 Javascript
js中string和number类型互转换技巧(分享)
2016/11/28 Javascript
vue.js 获取当前自定义属性值
2017/06/01 Javascript
ZeroClipboard.js使用一个flash复制多个文本框
2017/06/19 Javascript
使用原生js+canvas实现模拟心电图的实例
2017/09/20 Javascript
使用百度地图实现地图网格的示例
2018/02/06 Javascript
javaScript强制保留两位小数的输入数校验和小数保留问题
2018/05/09 Javascript
JavaScript实现一个带AI的井字棋游戏源码
2018/05/21 Javascript
关于jquery中attr()和prop()方法的区别
2018/05/28 jQuery
js使用formData实现批量上传
2020/03/27 Javascript
从组件封装看Vue的作用域插槽的实现
2019/02/12 Javascript
Vue 3.0 前瞻Vue Function API新特性体验
2019/08/12 Javascript
vue.js实现只能输入数字的输入框
2019/10/19 Javascript
详解elementUI中input框无法输入的问题
2020/04/27 Javascript
vue中使用vue-pdf的方法详解
2020/09/05 Javascript
Python离线安装PIL 模块的方法
2019/01/08 Python
基于python判断字符串括号是否闭合{}[]()
2020/09/21 Python
Python运算符+与+=的方法实例
2021/02/18 Python
几个判断型的面试题
2012/07/03 面试题
销售工作岗位职责
2013/12/24 职场文书
会计出纳岗位职责
2013/12/25 职场文书
餐厅总厨求职信
2014/03/04 职场文书
一个都不能少观后感
2015/06/04 职场文书
受欢迎的自荐信,就这么写!
2019/04/19 职场文书
详解Redis在SpringBoot工程中的综合应用
2021/10/16 Redis