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 相关文章推荐
用golang如何替换某个文件中的字符串
Apr 25 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
goland 清除所有的默认设置操作
Apr 28 Golang
Go使用协程交替打印字符
Apr 29 Golang
golang日志包logger的用法详解
May 05 Golang
go mod 安装依赖 unkown revision问题的解决方案
May 06 Golang
试了下Golang实现try catch的方法
Jul 01 Golang
Golang并发操作中常见的读写锁详析
Aug 30 Golang
golang实现浏览器导出excel文件功能
Mar 25 Golang
Go归并排序算法的实现方法
Apr 06 Golang
GO语言异常处理分析 err接口及defer延迟
Apr 14 Golang
Golang jwt身份认证
Apr 20 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 array_push()数组函数:将一个或多个单元压入数组的末尾(入栈)
2011/07/12 PHP
PHP--用万网的接口实现域名查询功能
2012/12/13 PHP
php实现邮件发送并带有附件
2014/01/24 PHP
对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析
2014/07/04 PHP
php函数与传递参数实例分析
2014/11/15 PHP
php正则去除网页中所有的html,js,css,注释的实现方法
2016/11/03 PHP
Yii框架引用插件和ckeditor中body与P标签去除的方法
2017/01/19 PHP
PHP基础之输出缓冲区基本概念、原理分析
2019/06/19 PHP
javascript 数据类型转换(parseInt,parseFloat)
2010/07/20 Javascript
Extjs TimeField 显示正常时间格式的代码
2011/06/28 Javascript
Javascript异步编程的4种方法让你写出更出色的程序
2013/01/17 Javascript
js统计页面的来访次数实现代码
2014/05/09 Javascript
使用jQuery和Bootstrap实现多层、自适应模态窗口
2014/12/22 Javascript
Bootstrap Table的使用总结
2016/10/08 Javascript
利用jquery给指定的table动态添加一行、删除一行的方法
2016/10/12 Javascript
javascript十六进制数字和ASCII字符之间的转换方法
2016/12/27 Javascript
关于vue.js过渡css类名的理解(推荐)
2017/04/10 Javascript
vue2.0实现导航菜单切换效果
2017/05/08 Javascript
JS中async/await实现异步调用的方法
2019/08/28 Javascript
webpack打包优化的几个方法总结
2020/02/10 Javascript
[47:04]EG vs RNG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
Python中的条件判断语句基础学习教程
2016/02/07 Python
简单谈谈python中的多进程
2016/11/06 Python
python字典DICT类型合并详解
2017/08/17 Python
Python3读取Excel数据存入MySQL的方法
2018/05/04 Python
Python实现去除图片中指定颜色的像素功能示例
2019/04/13 Python
Django 配置多站点多域名的实现步骤
2019/05/17 Python
python 中的[:-1]和[::-1]的具体使用
2020/02/13 Python
使用Python打造一款间谍程序的流程分析
2020/02/21 Python
利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)
2020/09/04 Python
python不同版本的_new_不同点总结
2020/12/09 Python
俄罗斯在线大型超市:ТутПросто
2021/01/08 全球购物
大四自我鉴定
2014/02/08 职场文书
办公室主任岗位竞聘书
2015/09/15 职场文书
优秀共产党员主要事迹材料
2015/11/05 职场文书
js作用域及作用域链工作引擎
2022/07/07 Javascript