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
模块, 提供的基础功能如下:
- 互斥锁
Mutex
- 读写锁
RWMutex
- 函数单次执行
Once
- 协程执行等待
WaitGroup
- 协程消息通知
Cond
- 多协程 map
Map
- 多协程对象池
Pool
几个都简单试过之后, 发现sync
模块针对常用的几个多协程工具进行了封装, 想来可以基本满足日常使用了.
终极通信-channel
channel
是一个协程安全的通信管道, 简单理解为数据从一侧放入, 从另一侧拿出. 这玩意感觉能玩出花来, 还不太理解, 留到国庆研究.
- AS3初学者容易迷糊的几个问题
- Spring Security笔记:自定义Login/Logout Filter、AuthenticationProvider、AuthenticationToken
- ASP.NET Web API 2.1支持Binary JSON(Bson)
- Spring Security笔记:自定义登录页
- 浅析 Linux 初始化 init 系统
- 如何提高Python运行效率 超实用的四种提速方法
- 如何让oracle的select强制走索引
- ActionScript3.0(AS3)中的泛型数组Vector
- 人民网评:“算法推荐”不能成为传播低俗信息的助推器
- 代码实现WordPress自动关键词keywords与描述description
- 基于Spring的简易SSO设计
- MS 的IOC容器(ObjectBuilder)?
- 自动驾驶五问
- 开放的即时通信协议Jabber
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- HDU Problem D [ Humble number ]——基础DP丑数序列
- Java开发编程规范: 3.代码格式
- flex-direction
- css中清除浮动方式五
- css中-清除浮动方式四
- forin forof forEach myForEach
- 深拷贝,你懂吗?
- Codeforce-CodeCraft-20 (Div. 2)-C. Primitive Primes(本原多项式+数学推导)
- DOM事件机制(原理级别的)
- 杭电60题--part 1 HDU1003 Max Sum(DP 动态规划)
- js的的的图片随屏幕滚动而滑入滑出的效果(万 万。。。字长文)
- Codeforce-CodeCraft-20 (Div. 2)-B. String Modification (找规律+模拟)
- Codeforce-CodeCraft-20 (Div. 2)-A. Grade Allocation
- Cypress系列(69)- route() 命令详解
- Codeforce-Ozon Tech Challenge 2020-D. Kuroni and the Celebration(交互题+DFS)