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

  • 相关阅读:
    'Undefined symbols for architecture i386,clang: error: linker command failed with exit code 1
    The codesign tool requires there only be one 解决办法
    XCode iOS project only shows “My Mac 64bit” but not simulator or device
    Provisioning profile XXXX can't be found 的解决办法
    UIView 中的控件事件穿透 Passthrough 的实现
    Xcode4.5出现时的OC新语法
    xcode 快捷键(持续更新)
    打越狱包
    php缓存与加速分析与汇总
    浏览器的判断
  • 原文地址:https://www.cnblogs.com/hujingnb/p/13756785.html
Copyright © 2011-2022 走看看