go常用包——sync

内容

  • Atomic
  • Mutex
  • RWMutex
  • WaitGroup
  • Once
  • Sync.Map
  • Sync.Pool
  • Cond

Atomic

go中atomic 包实现原子操作

  • 基本类型的原子操作有6种:int32, int64, uint32, uint64, uintptr, unsafe.Pinter;操作类型有5种:增减, 比较并交换, 载入, 存储,交换
  • Value提供任意类型的原子操作,操作类型有:载入和存储
基本数据类型:
    //增减操作
    var a int32;
    //增操作
    new_a := atomic.AddInt32(&a, 1);

    //减操作
    new_a = atomic.AddInt32(&a, -1);
 
    //CAS(Compare And Swap)比较并交换操作
    //函数名以CompareAndSwap为前缀,并具体类型名
    //函数会先判断参数一指向的值与参数二是否相等,如果相等,则用参数三替换参数一的值。
    var b int32;
    atomic.CompareAndSwapInt32(&b, 0, 3);
 
    //载入操作
    //当我们对某个变量进行读取操作时,可能该变量正在被其他操作改变,或许我们读取的是被修改了一半的数据。
    //所以我们通过Load这类函数来确保我们正确的读取
    //函数名以Load为前缀,加具体类型名
    var c int32;
    tmp := atomic.LoadInt32(&c);

    //存储操作
    //与载入函数相对应,提供原子的存储函数; 函数名以Store为前缀,加具体类型名
    var d int32;
    //存储某个值时,任何CPU都不会都该值进行读或写操作
    //存储操作总会成功,它不关心旧值是什么,与CAS不同
    atomic.StoreInt32(&d, 666);

任意类型:
提供了两个方法,保证原子的存储和读取(并发操作时,保证不会读到正在写入到一半的值)
1 func (v *Value) Load() (x interface{})
2 func (v *Value) Store(x interface{})

    type AtomicStruct struct {
        Age int
        name string
    }
    config := atomic.Value{} // 1 声明一个value类型变量,相当于一个容器
    config.Store(&AtomicStruct{})  // 2 说明这个容器存储的是AtomicStruct 指针类型,一旦说明后,后续的store操作只能是这个类型

    wg := sync.WaitGroup{}
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func(i int) {
            defer wg.Done()
            if i == 5 {
                name := "name" + string(i)
                config.Store(&AtomicStruct{Age: i, name: name})
            }
            fmt.Println(config.Load())
        }(i)
    }
    wg.Wait()

Mutex

互斥锁: go中通过sync.Mutex的零值来表示一个没有被锁定的互斥量,开箱即用,申明一个变量就可以用,但是如果把互斥量当做入参传入到函数中使用时,只能传递指针,不能是值传递,值传递是拷贝,不能达到锁住的作用。

var x int 
func main()  {
var m sync.Mutex
add(&m, 2)
}

// 同步的做甲方
func add(m *sync.Mutex, value int){
m.Lock()
x = x + value
m.Unlock()
}

RWMutex

读写锁: go中通过sync.RWMutex的零值来表示一个读写锁实例,读写锁中有两对方法,分别是读锁定和读解锁、写锁定和写解锁

  • 规则:读写锁下,多个写操作之间是互斥的,写操作和读操作之间也是互斥的,但是多个读操作之间不存在互斥关系
rw.Lock() // 写锁定
rw.Unlock()
rw.RLock() // 读锁定
rw.RUnlock()

func main()  {
   var rw sync.RWMutex
   go func() {
      rw.RLock()
      println(1)
      time.Sleep(2*time.Second)
      rw.RUnlock()
      println(2)
   }()
   time.Sleep(1*time.Second)
   println(3)
    // 被堵在这里,因为协程中 rw.RLock()加了读锁,会阻塞写锁,不会堵塞其他协程获取读锁,rw.RUnlock()执行后,释放了读锁,同样也会释放写锁,前提是已经没有任何其他的读锁了;如果是先执行rw.Lock(),同样也会堵塞其他写锁和读锁,rw.Ulock()会把读锁和写锁全部释放
   rw.Lock()
   println(4)
}

WaitGroup

  • wg会阻塞在wait()方法上,等待一组协程执行结束,同样是开箱即用,在函数中当做入参传递时,记住传递指针
func main()  {
   var wg sync.WaitGroup
   wg.Add(1)
   go func() {
      println(1)
      wg.Done()
   }()

   //主协程阻塞在这里,必定会等协程中执行完毕后,才会执行打印2
   wg.Wait()
   println(2)
}

Once

  • sync.Once 同样开箱即用,申明即可, Do方法接收一个无参数、无结果的函数作为参数,保证这个方法只执行一次
  • 原理是Once结构体内维护了一个无符号的变量done,当执行一次func后会原子的加1,如果done不会0时,就不会执行func了
 var once sync.Once //同样开箱即用
// Do方法接收一个无参数、无结果的函数作为参数,保证这个方法只执行一次
 Once.Do(func(){
    fmt.println(“func方法只执行一次")
})

// 原理是Once结构体内维护了一个无符号的变量done,当执行一次func后会原子的加1,如果done不会0时,就不会执行func了

func (o *Once) Do(f func()) {
   if atomic.LoadUint32(&o.done) == 0 {
      // Outlined slow-path to allow inlining of the fast-path.
      o.doSlow(f)
   }
}
func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   if o.done == 0 {
      defer atomic.StoreUint32(&o.done, 1)
      f()
   }
}

sync.Map

  • 并发安全的map
func main(){
    var sm sync.Map
    // 写入
    sm.Store("a", 1)
    sm.Store("b", 2)
    sm.Store("c", 3)

    // 取出
    v, _ := sm.Load("a")
    fmt.Println(v.(int))

    // 删除
    sm.Delete("b")

    // 存在则获取 不存在则添加
    sm.LoadOrStore("d", 4)

    // 遍历
    sm.Range(func(key, value interface{}) bool {
        fmt.Println(key.(string), value.(int))
        return true
    })
}

Sync.Pool

  • Pool用来做一个临时对象池,当某个对象会被经常创建或是在并发场景下多个协程会会创建相同的对象,此时可以考虑用sync.Pool来优化性能,避免大量对象的创建 销毁引起的GC问题。
  • Pool只有两个方法,Get和Put, Get从对象池中获取对象,如果不存在则用初始化对象池是赋予的New方法创建一个对象返回, Put方法用于当用完对象后,把对象归还到对象池,如果对象需要保持初始化状态,则用完对象后,应该对对象做一些清零的逻辑,然后在归还到池子中,否则下一次get是,获取到的对象会保存了上一次put的记录;
  • Pool不适于做连接池之类的,因为pool会在GC是被回收的;
  • Pool是并发安全的;
    举个例子
func SyncPoolExample()  {
    pool := sync.Pool{
        New: func() interface{} {
            fmt.Println("aa")
            return People{}
        },
    }
    for i:=0; i < 3; i++ {
        go func() {
            people := pool.Get().(People)
            fmt.Println(people)
            people.name = "li"
            fmt.Println(people)
            people.name = ""
            pool.Put(people)
        }()
        time.Sleep(time.Second * 2)
    }
    time.Sleep(time.Second * 10)
}

aa
{0 }
{0 li}
aa
{0 }
{0 li}
{0 }
{0 li}
当协程还没有归还对象到池子里时,如果其他协程此时来get,则就会新建一个

//example 2
func main() {
    pool := sync.Pool{
        New: func() interface{} {
            b := make([]byte, 0, 1024)
            return b
        },
    }
    b := pool.Get().([]byte)
    b = append(b, 0xff)

    b = b[:0] // 用完后,清空[]byte,在归还池子
    pool.Put(b)
}

实现原理:

Cond

条件变量,类似java object中的wait notify和notifyAll方法,Cond也提供了三个方法Wait、Singal、Broadcast

func SyncCondExample(){
    cond := sync.NewCond(new(sync.Mutex))
    for i := 0; i < 10; i++ {
        go func(i int) {
            cond.L.Lock()
            cond.Wait()
            fmt.Println("goroutine: ", i)
            cond.L.Unlock()
        }(i)
    }
    fmt.Println("all goroutines wait...")
    time.Sleep(time.Second * 3)
    //  按等待顺序 释放一个
    //cond.Signal()
    // 释放所有
    cond.Broadcast()
    time.Sleep(time.Second * 3)
}

all goroutines wait...
goroutine:  4
goroutine:  8
goroutine:  5
goroutine:  1
goroutine:  0
goroutine:  7
goroutine:  3
goroutine:  2
goroutine:  6
goroutine:  9
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352