zoukankan      html  css  js  c++  java
  • go语法:sync

    参考:

    https://blog.csdn.net/li_101357/article/details/80286549(CSDN:sync包)

    https://zhuanlan.zhihu.com/p/138214620(知乎:sync包应用详解)

    https://studygolang.com/articles/3373(Mutex和rwmutex的区别)

    在并发编程中同步原语也就是我们通常说的锁的主要作用是保证多个线程或者 goroutine在访问同一片内存时不会出现混乱的问题。Go语言的sync包提供了 MutexRWMutexWaitGroupOnce 和 Cond 这些同步原语的实现原理。

    本片只聚焦sync包里这些同步原语的应用场景,同时也会介绍sync包中的PoolMap(另外整理)的应用场景和使用方法。

     sync.Mutex(互斥锁)

    func mutex()  {
        type safeInt struct{
            &sync.Mutex
            Num int
        }
        count:=&safeInt{}
        done:=make(chan bool)
    
        for i:=0;i<10000;i++{
            go func(i int) {
                count.Lock()
                count.Num+=i
                count.Unlock()
                done<-true
            }(i)
        }
        for i:=0;i<10000;i++ {
            <-done
        }
        fmt.Println(count.Num)
    
    }

    sync.RWMutex(读写锁)

    func rwMutex()  {
        type safeInt struct{
            &sync.RWMutex
            Num int
        }
        count:=&safeInt{}
        done:=make(chan bool)
    
        for i:=0;i<10000;i++{
            go func(i int) {
                count.Lock()//写锁
                count.Num+=i
                count.Unlock()//写锁
                done<-true
            }(i)
        }
        for i:=0;i<10000;i++ {
            <-done //为了主协程等待
        }
        fmt.Println(count.Num)
    }

     

    sync.WaitGroup(等待组)

    func waitGroup()  {
        wg := &sync.WaitGroup{}
    
        for i := 0; i < 8; i++ {
            wg.Add(1)
            go func(i int) {
                // Do something
                fmt.Println(i)
                wg.Done()
            }(i)
        }
        wg.Wait()
        fmt.Println("done")
    }

     

    sync.Once(执行一次)

    func once()  {
    
        once:=&sync.Once{}
    
        ch:=make(chan bool)
        for i:=0;i<10;i++ {
            go func() {
                once.Do(func() {
                    fmt.Println("我自会执行一次")
    
                })
                ch<-true
            }()
        }
        for i:=0;i<10;i++ {
            <-ch
        }
    }

     

    sync.Cond(条件变量)

    在 Wait 之前应当手动为 c.L 上锁,Wait 结束后手动解锁。为避免虚假唤醒,需要将 Wait 放到一个条件判断循环中。官方要求的写法如下:

    //控制方法
    cond:=sync.NewCond(&mutex):生成一个cond,需要传入一个mutex,因为阻塞等待通知的操作以及通知解除阻塞的操作就是基于sync.Mutex来实现的。
    cond.Wait():用于等待通知
    cond.Signal():用于发送单个通知
    cond.Broadcat():用于广播
    
    //使用方式
    cond.L.Lock()
    for !condition{
      cond.Wait()  
    }
    cond.L.UnLock()
    //满足条件后的后续逻辑

    举例

    func cond()  {
        var locker sync.Mutex
        var cond = sync.NewCond(&locker) //等价于 var cond = &sync.Cond{L:&sync.Mutex{}}
        //locker.Lock()
        for i := 0; i < 10; i++ {
            go func(x int) {
                cond.L.Lock()         // 获取锁
                defer cond.L.Unlock() // 释放锁
                cond.Wait()           // 等待通知,阻塞当前 goroutine
                // 通知到来的时候, cond.Wait()就会结束阻塞, do something. 这里仅打印
                fmt.Println(x)
            }(i)
        }
        time.Sleep(time.Second * 1) // 睡眠 1 秒,等待所有 goroutine 进入 Wait 阻塞状态
        fmt.Println("Signal...")
        cond.Signal()               // 1 秒后下发一个通知给已经获取锁的 goroutine
    
        time.Sleep(time.Second * 1)
        fmt.Println("Signal...")
        cond.Signal()               // 1 秒后下发下一个通知给已经获取锁的 goroutine
    
        time.Sleep(time.Second * 1)
        cond.Broadcast()            // 1 秒后下发广播给所有等待的goroutine
    
        fmt.Println("Broadcast...")
        time.Sleep(time.Second * 1) // 睡眠 1 秒,等待所有 goroutine 执行完毕
    }
    func cond2()  {
    mutex := sync.Mutex{}
    var cond = sync.NewCond(&mutex)
    mail := 0
    go func() {
    for count := 0; count <= 5; count++{
    time.Sleep(time.Second)
    mail = count
    cond.Broadcast()
    }
    }()
    //为什么使用for而不是if,if会同时上锁,广播后会同时解锁
    //broadcast的时候,会通知到所有的worker,此时wait都会解除,但并不是所有的worker都满足通知条件的,所以加一个for循环,不满足通知条件的会再次wait。

    // worker1
    go func() {
    cond.L.Lock()
    for mail != 1 { // 触发的条件,如果不等于1,就会进入cond.Wait()等待,此时cond.Broadcast()通知进来的时候,wait阻塞解除,进入下一个循环,此时发现mail == 1,跳出循环,开始工作。
    cond.Wait()//会阻塞
    }
    cond.L.Unlock()
    fmt.Println("worker1 started to work")
    time.Sleep(3*time.Second)
    fmt.Println("worker1 work end")
    }()
    // worker2
    go func() {
    cond.L.Lock()
    for mail != 4 {
    cond.Wait()
    }
    cond.L.Unlock()
    fmt.Println("worker2 started to work")
    time.Sleep(3*time.Second)
    fmt.Println("worker2 work end")
    }()
    // worker3
    go func() {
    cond.L.Lock()
    for mail != 5 {
    cond.Wait()
    }
    cond.L.Unlock()
    fmt.Println("worker3 started to work")
    time.Sleep(3*time.Second)
    fmt.Println("worker3 work end")
    }()
    time.Sleep(10*time.Second)
    }

     

    sync.Map

    参考:https://blog.csdn.net/u010230794/article/details/82143179(csdn:sync.Map的使用和介绍)

    利用传统的sync.RWMutex+Map实现并发安全的map

    //传统方式并发安全的map
        var rwmap = struct {
            sync.RWMutex
            m map[string]string
        }{m:make(map[string]string)}
        
        //写数据时
        rwmap.Lock()
        rwmap.m["key"]="value123"
        rwmap.Unlock()
        
        //读数据时
        rwmap.RLock()
        val:=rwmap.m["key"]
        rwmap.RUnlock()
        fmt.Println(val)

    使用sync.Map实现

    func syncmap()  {
        m := &sync.Map{}
    
        // 添加元素
        m.Store(1, "one")
        m.Store(2, "two")
    
        // 获取元素1
        value, contains := m.Load(1)
        if contains {
            fmt.Printf("%s
    ", value.(string))
        }
    
        // 返回已存value,否则把指定的键值存储到map中
        value, loaded := m.LoadOrStore(3, "three")
        if !loaded {
            fmt.Printf("%s
    ", value.(string))
        }
    
        m.Delete(3)
    
        // 迭代所有元素
        m.Range(func(key, value interface{}) bool {
            fmt.Printf("%d: %s
    ", key.(int), value.(string))
            return true
        })
    }
  • 相关阅读:
    分布式配置 SSH 免密登陆
    转载--宏观认识大数据圈
    转载--存储是怎样炼成的
    转载--关于hdfs
    绕不开的hadoop
    sqoop 使用
    Excel VBA解读(54):排序——Sort方法
    MSSQL附加数据库时提示以下错误: 无法打开物理文件“***.mdf”。操作系统错误 5:“5(拒绝访问。)”。 (Microsoft SQL Server,错误: 5120)
    Delphi Code Editor 之 编辑器选项
    解决StrToDateTime()不是有效日期类型的问题
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/15185118.html
Copyright © 2011-2022 走看看