• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

go语法:sync

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

参考:

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.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\n", value.(string))
    }

    // 返回已存value,否则把指定的键值存储到map中
    value, loaded := m.LoadOrStore(3, "three")
    if !loaded {
        fmt.Printf("%s\n", value.(string))
    }

    m.Delete(3)

    // 迭代所有元素
    m.Range(func(key, value interface{}) bool {
        fmt.Printf("%d: %s\n", key.(int), value.(string))
        return true
    })
}

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Go websocket 聊天室demo以及k8s 部署发布时间:2022-07-10
下一篇:
GO汇总发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap