Go的cond和livelock
cond就是condition的缩写,最近在看的一本书顶上就有很多cond的使用,一看Golang中也有,便小看一下。一句话概括就是条件。
https://ieevee.com/tech/2019/06/15/cond.html
两个例子
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 的话,不一致的加锁顺序会直接导致死锁。
一个没有高级趣味的人。
email:hushui502@gmail.com