zoukankan      html  css  js  c++  java
  • golang 多协程的同步方法总结

    之前用 go 写一个小工具的时候, 用到了多个协程之间的通信, 当时随手查了查, 结果查出来一大坨, 简单记录一下. golang中多个协程之间是如何进行通信及数据同步的嘞.

    共享变量

    一个最简单, 最容易想到的, 就是通过全局变量的方式, 多个协程读写同一个变量. 但对同一个变量的更改, 就不得不加锁了, 否则极易引发数据问题. 一般系统库都提供基本的锁, go 也提供了.

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    var num = 0
    // 互斥锁
    var mutex = sync.Mutex{}
    // 读写锁
    var rwMutex = sync.RWMutex{}
    
    func main() {
    	for i := 0; i < 100; i++ {
    		go incrNum()
    	}
    	time.Sleep(2)
    	fmt.Println(num)
    }
    
    func incrNum() {
    	mutex.Lock()
    	num = num + 1
    	mutex.Unlock()
    }
    

    仅执行一次

    当查询锁查到sync这个模块时, 发现它下面的对象并没有几个, 都是针对协程同步的各个方面给出的解决方案. 所以我就一个一个看文档试了试.

    当你需要对环境, 连接池等等资源进行初始化时, 这种操作只需要执行一次, 这时候就需要它了. sync.Once对象可以保证仅执行一次. 和 init 方法有些类似, 不过 init 方法是在模块首次加载时执行, 而sync.Once是在首次调用时执行. (其实现就是一个计数器加一个互斥锁)

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    var num = 0
    var once = sync.Once{}
    
    func main() {
    	for i := 0; i < 100; i++ {
    		go once.Do(incrNum)
    	}
    	time.Sleep(2)
    	fmt.Println(num)
    }
    
    func incrNum() {
    	num = num + 1
    }
    

    等待其他协程处理

    某个协程需要等第一阶段的所有协程处理完毕, 才能开始执行第二阶段. 这个时候, 等待其他协程就可以通过sync.WaitGroup 来实现. (当然, 也可以通过一个共享计数器变量来实现).

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var waitGroup = sync.WaitGroup{}
    
    func main() {
    	for i := 0; i < 100; i++ {
    		go incrNum()
    	}
    	// 等待其他协程处理完毕(共享变量为0)
    	waitGroup.Wait()
    	fmt.Println("don")
    }
    
    func incrNum() {
    	// 增加需要等待的协程数量(共享变量+1)
    	waitGroup.Add(1)
    	// do something
    	// 标记当前协程处理完成(共享变量-1)
    	waitGroup.Done()
    }
    

    消息通知

    多个协程启动时, 等待某个命令到来时执行命令, 唤醒等待协程. go 对此类操作也进行了处理, 感觉好贴心哦. 但是经过测试, 即使没有空闲的协程, 唤醒命令同样能够发出去, 所以需要注意一下.

    package main
    
    import (
    	"sync"
    )
    
    var mutex = &sync.Mutex{}
    var cond = sync.NewCond(mutex)
    
    func main() {
    	for i := 0; i < 100; i++ {
    		go incrNum()
    	}
    	// 发送命令给一个随机获得锁的协程
    	cond.Signal()
    	// 发送命令给所有获得锁的协程
    	cond.Broadcast()
    }
    
    func incrNum() {
    	// 获取锁, 标识当前协程可以处理命令
    	cond.L.Lock()
    	// 可添加退出执行命令队列的条件
    	for true {
    		// 等待命令
    		cond.Wait()
    		// do something
    	}
    	// 释放锁, 标记命令处理完毕, 退出协程
    	cond.L.Unlock()
    }
    

    多协程 map

    普通的 map 在多协程操作时, 是不支持并发写入的. go贴心的给封装了支持并发写入的map. 同时也提供了针对map的基本操作.

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    var m = sync.Map{}
    
    func main() {
    	for i := 0; i < 100; i++ {
    		go func() {
    			m.Store("1", 1)
    		}()
    	}
    	time.Sleep(time.Second * 2)
    	// 遍历 map
    	m.Range(func(key, value interface{}) bool {
    		// 返回 false 结束遍历
    		return true
    	})
    	// 读取变量, 若不存在则设置
    	m.LoadOrStore("1", 3)
    	// 删除 key
    	m.Delete("1")
    	// 读取变量
    	load, _ := m.Load("1")
    	fmt.Println(load)
    }
    
    

    多协程对象池

    对于数据库连接池应该并不陌生. 而sync.Pool对象是go封装的协程安全的对象池. 对象池的使用十分简单, 存/取

    package main
    
    import (
    	"sync"
    )
    
    var p = sync.Pool{
    	// 当池子中没有对象了, 用于创建新对象
    	New: func() interface {}{
    		return "3"
    	},
    }
    
    func main() {
    	// 从池子中获取一个对象
    	r := p.Get()
    	// 用完后将对象放回池子中
    	p.Put(r)
    }
    

    sync 简单总结

    针对go系统的sync模块, 提供的基础功能如下:

    1. 互斥锁 Mutex
    2. 读写锁 RWMutex
    3. 函数单次执行 Once
    4. 协程执行等待 WaitGroup
    5. 协程消息通知 Cond
    6. 多协程 map Map
    7. 多协程对象池 Pool

    几个都简单试过之后, 发现sync模块针对常用的几个多协程工具进行了封装, 想来可以基本满足日常使用了.

    终极通信-channel

    channel是一个协程安全的通信管道, 简单理解为数据从一侧放入, 从另一侧拿出. 这玩意感觉能玩出花来, 还不太理解, 留到国庆研究.

  • 相关阅读:
    CentOS-6.9搭建NFS服务器
    CentOS6.9-搭建vsftpd三种模式及参数详解
    CentOS-7.3 MySQL数据库入门简介及源码编译安装MySQL服务
    CentOS7.3系统vsftpd服务简介及vsftpd的三种模式的搭建
    安装CentOS7.4 Linux操作系统
    windows server2008R2 64位 配置 mysql-8.0.15-winx64
    怎么删除服务中的mysql服务
    mysql-8.0.15-winx64 解压版安装 图文详解
    命令行下创建MySQL数据库与创建用户以及授权
    windows 2008r2+php5.6.28环境搭建详细过程
  • 原文地址:https://www.cnblogs.com/hujingnb/p/13756785.html
Copyright © 2011-2022 走看看