博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
sync
阅读量:6237 次
发布时间:2019-06-22

本文共 3918 字,大约阅读时间需要 13 分钟。

sync包有以下几个内容:

(1)sync.Pool 临时对象池
(2)sync.Mutex 互斥锁
(3)sync.RWMutex 读写互斥锁
(4)sync.WaitGroup 组等待
(5)sync.Cond 条件等待
(6)sync.Once 单次执行

一、临时对象池

Pool可以用来存储临时对象,其实原理就是这个对象池指向对象变量,以防没有变量指向对象时,被GC所回收。其目的时为了避免重复创建相同的对象造成GC的负担,其中存放的临时对象随时可能被GC回收掉(如果该对象不再被其它变量引用)。
从Pool中取出对象时,如果Pool中没有对象,将回nil,但是如果给Pool。New字段指定一个函数的话,Pool将使用函数创建一个新对象返回。
Pool可以安全的在多个协程中并行使用,但Pool并不适用于所有空闲对象,Pool应该用来管理并发的协程共享的临时对象,而不应该管理短寿命对象中的临时对象,因为这种情况下内存不能很好的分配,这些短寿命对象应该自己实现空闲列表。

type Pool struct {        // 创建临时对象的函数        New func() interface{}}// 向临时对象池中存入对象func (p *Pool) Put() x interface{}// 向临时对象池中取出对象func (p *Pool) Get() interface{}

案例:

package mainimport (    "fmt"    "sync")func main() {    var pool sync.Pool    var val interface{}    pool.Put("1")    pool.Put(12)    pool.Put(true)    for {        val = pool.Get()        if val == nil {            break        }        fmt.Println(val)    }}

二、互斥锁

互斥锁用来保证再任一时刻,只能有一个协程访问某对象。Mutex的初始值为解锁状态,Mutex通常作为其它结构体的匿名字段使用,使该结构体具有Lock和Unlock方法。
Mutex可以安全的再多个协程中并行使用。

注意:如果对未加锁的进行解锁,则会引发panic。

加锁,保证数据能够正确:

package mainimport (    "fmt"    "sync")var num intvar mu sync.Mutexvar wg sync.WaitGroupfunc main() {    wg.Add(10000)    for i := 0; i < 10000; i++ {        go Add()    }    wg.Wait()    fmt.Println(num)}func Add() {    mu.Lock() // 加锁    defer func() {        mu.Unlock() // 解锁        wg.Done()    }()    num++}

并发没有加锁,如下代码,当你执行下面代码时,会发现结果不等于10000,其原因是因为出现这样的情况,就是其中一些协程刚好读取了num的值,此时该协程刚好时间片结束,被挂起,没有完成加1。然后其他协程进行加1,然后当调度回到原来那个协程,num = 原来的那个num(而不是最新的num) + 1,导致num数据出错:

package mainimport (    "fmt"    "sync")var num intvar wg sync.WaitGroupfunc main() {    wg.Add(10000)    for i := 0; i < 10000; i++ {        go Add()    }    wg.Wait()    fmt.Println(num)}func Add() {    defer wg.Done()    num++}

三、读写互斥锁

RWMutex比Mutex多了一个“写锁定” 和 “读锁定”,可以让多个协程同时读取某对象。RWMutex的初始值为解锁状态。RWMutex通常作为其它结构体的匿名字段使用。
RWMutex可以安全的在多个协程中并行使用。

// Lock 将 rw 设置为写状态,禁止其他协程读取或写入func (rw *RWMutex) Lock()// Unlock 解除 rw 的写锁定状态,如果rw未被锁定,则该操作会引发 panic。func (rw *RWMutex) Unlock()// RLock 将 rw 设置为锁定状态,禁止其他协程写入,但可以读取。func (rw *RWMutex) RLock()// Runlock 解除 rw 设置为读锁定状态,如果rw未被锁定,则该操作会引发 panic。func (rw *RWMutex) RUnLock()// RLocker 返回一个互斥锁,将 rw.RLock 和 rw.RUnlock 封装成一个 Locker 接口。func (rw *RWMutex) RLocker() Locker

四、组等待

WaitGroup 用于等待一组协程的结束。主协程在创建每个子协程的时候先调用Add增加等待计数,每个子例程在结束时调用 Done 减少例程计数。之后,主协程通过 Wait 方法开始等待,直到计数器归零才继续执行。

// 计数器增加 delta,delte可以时负数func (wg *WaitGroup) Add(delta int)// 计数器减少1,等价于Add(-1)func (wg *WaitGroup) Done()// 等待直到计数器归零。如果计数器小于0,则该操作会引发 panic。func (wg *WaitGroup) Wait()

五、条件等待

条件等待和互斥锁有不同,互斥锁是不同协程公用一个锁,条件等待是不同协程各用一个锁,但是wait()方法调用会等待(阻塞),直到有信号发过来,不同协程是共用信号

package mainimport (    "fmt"    "sync"    "time")func main() {    var wg sync.WaitGroup    cond := sync.NewCond(new(sync.Mutex))    for i := 0; i < 3; i++ {        go func(i int) {            fmt.Println("协程", i, "启动。。。")            wg.Add(1)            defer wg.Done()            cond.L.Lock()            fmt.Println("协程", i, "加锁。。。")            cond.Wait()            fmt.Println("协程", i, "解锁。。。")            cond.L.Unlock()        }(i)    }    time.Sleep(2e9)    cond.L.Lock()    fmt.Println("主协程发送信号量。。。")    cond.Signal()    cond.L.Unlock()    time.Sleep(2e9)    cond.L.Lock()    fmt.Println("主协程发送信号量。。。")    cond.Signal()    cond.L.Unlock()    time.Sleep(2e9)    cond.L.Lock()    fmt.Println("主协程发送信号量。。。")    cond.Signal()    cond.L.Unlock()    wg.Wait()}

六、单次执行

Once的作用是多次调用但只执行一次,Once只有一个方法,Once.Do(),向Do传入一个函数,这个函数在第一次执行Once.Do()的时候会被调用,以后再执行Once.Do()将没有任何动作,即使传入了其他的函数,也不会被执行,如果要执行其它函数,需要重新创建一个Once对象。
Once可以安全的再多个协程中并行使用。是协程安全的

标准库中原型:// 多次调用仅执行一次指定的函数ffunc (o *Once) Do(f func())// 示例:Oncepackage mainimport (    "fmt"    "sync")func main() {    var once sync.Once    var wg sync.WaitGroup    onceFunc := func() {        fmt.Println("hello")    }    wg.Add(10)    for i := 0; i < 10; i++ {        go func() {            defer wg.Done()            once.Do(onceFunc) // 多次调用只执行一次        }()    }    wg.Wait()}

参考:

(1)
(2)

转载地址:http://hbkia.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
“对人不对事”和“对事不对人”
查看>>
CSS实现pre标签中内容换行方法
查看>>
领导牛B轰轰的 https 介绍应用
查看>>
Centos7下的Openssl和CA
查看>>
windows 2003防止网卡被禁用的设置
查看>>
lvs健康检测脚本
查看>>
马哥运维架构 第五周作业
查看>>
如何使用netstat命令验证DDOS***
查看>>
【zabbix学习笔记之三】部署zabbix-agent端
查看>>
找到数组中第i大、第i小的值
查看>>
我的友情链接
查看>>
ceph源码片段——c++引用
查看>>
list 之 增 删 改 查 排序 合并
查看>>
python模块paramiko的上传下载和远程执行命令方法
查看>>
SAS硬盘和SATA硬盘的介绍和区别
查看>>
开源TMS团队协作web系统
查看>>
linux批量修改文件夹,文件的权限,所属用户,用户组
查看>>
qt5.0.2 + opencv244
查看>>
elasticsearch日期类型
查看>>