Go的cond和livelock

cond就是condition的缩写,最近在看的一本书顶上就有很多cond的使用,一看Golang中也有,便小看一下。一句话概括就是条件。

 

https://ieevee.com/tech/2019/06/15/cond.html

https://xargin.com/livelock/

 

两个例子

package main

import (
    "fmt"
    "sync"
    "time"
)

var sharedRsc = false

func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    m := sync.Mutex{}
    c := sync.NewCond(&m)

    go func() {
        c.L.Lock()
        for sharedRsc == false {
            fmt.Println("goroutine1 wait")
            c.Wait()
        }
        fmt.Println("gorountie1 ", sharedRsc)
        c.L.Unlock()
        wg.Done()
    }()

    go func() {
        // this go routine wait for changes to the sharedRsc
        c.L.Lock()
        for sharedRsc == false {
            fmt.Println("goroutine2 wait")
            c.Wait()
        }
        fmt.Println("goroutine2", sharedRsc)
        c.L.Unlock()
        wg.Done()
    }()

    time.Sleep(2 * time.Second)
    c.L.Lock()
    fmt.Println("main goroutine ready")
    sharedRsc = true
    c.Broadcast()
    fmt.Println("main goroutine broadcast")
    c.L.Unlock()
    wg.Wait()
}

输出

goroutine1 wait
main goroutine ready
main goroutine broadcast
gorountie1  true
goroutine2 true

这个解释也很简单就是这个c是一个公用的,c.wait必须要broadcast唤醒。这里的一个小问题是加锁解锁,似乎要人为的关注顺序,否则就会造成活锁

 

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
    "time"
)

func main() {
    cadence := sync.NewCond(&sync.Mutex{})
    go func() {
        for range time.Tick(1*time.Microsecond) {
            cadence.Broadcast()
        }
    }()

    takeStep := func() {
        cadence.L.Lock()
        cadence.Wait()
        cadence.L.Unlock()
    }

    tryDir := func(name string, dirName string, dir *int32) bool {
        fmt.Printf(name + " " + dirName + "\n")
        atomic.AddInt32(dir, 1)
        takeStep()
        if atomic.LoadInt32(dir) == 1 {
            fmt.Printf("success")
            return true
        }
        takeStep()
        atomic.AddInt32(dir, -1)
        return false
    }

    var left, right int32
    tryLeft := func(name string) bool {
        return tryDir(name, "left", &left)
    }
    tryRight := func(name string) bool {
        return tryDir(name, "right", &right)
    }
    walk := func(wg *sync.WaitGroup, name string) {
        defer wg.Done()
        fmt.Printf("%v is trying to scoot: ", name)
        for i := 0; i < 5; i++ {
            if tryLeft(name) || tryRight(name) {
                return
            }
        }
        fmt.Printf("\n%v tosses her hands up in exasperation!", name)
    }

    var wg sync.WaitGroup
    wg.Add(2)
    go walk(&wg, "alice")
    go walk(&wg, "bob|")

    wg.Wait()
}

 

 

这里我们就是模仿两个协程来竞争一个原子变量,这里的takeStep就是加锁解锁阻塞的关键。

活锁产生的原因就是并发场景下大家没有协调好加锁顺序,能产生活锁的另一个前提是获取锁用的是 trylock,所谓 trylock,就是 try 一下,如果没 lock 住,就返回加锁失败。如果不是 trylock 的话,不一致的加锁顺序会直接导致死锁。

posted @ 2020-06-28 13:41  zhangyu63  阅读(343)  评论(0编辑  收藏  举报