sync.Cond
go/sync
sync.Cond用于goroutine之间的合作,用于协程的挂起和唤醒。当多个协程之间合作时,有可能一个协程须要依赖别的协程实现后再进行某种操作,这时可根据管道进行通信,这对于两个协程之间是比拟不便的。但若是多个协程依赖一个协程的实现后再进行某种操作,那么就能够应用sync.Cond来实现了;
例子:
package mainimport ( "fmt" "sync" "time")var locker = new(sync.Mutex)var cond = sync.NewCond(locker)func main() { for i := 0; i < 40; i++ { go func(x int) { cond.L.Lock() //获取锁 cond.Wait() //期待告诉,阻塞以后goroutine cond.L.Unlock() //开释锁 fmt.Println(x) time.Sleep(time.Second * 1) }(i) } time.Sleep(time.Second * 1) fmt.Println("Signal...") cond.Signal() // 下发一个告诉给曾经获取锁的goroutine time.Sleep(time.Second * 1) cond.Signal() // 3秒之后 下发一个告诉给曾经获取锁的goroutine time.Sleep(time.Second * 3) cond.Broadcast() //3秒之后 下发播送给所有期待的goroutine fmt.Println("Broadcast...") time.Sleep(time.Second * 60)}
协程在应用cond时,首先须要对cond持有的锁进行加锁操作,而后调用Wait函数期待,后续操作会阻塞在Wait处;当主协程决定唤醒期待的协程时,它会应用Signal(随机唤醒一个期待的协程)或Broadcast(唤醒所有期待的协程)唤醒期待的协程。当某一协程被唤醒后,Wait函数会返回,此时须要解锁,之后便能够做后续的操作了;
实现细节:
cond的数据结构
type Cond struct { noCopy noCopy // L is held while observing or changing the condition L Locker notify notifyList checker copyChecker}
Cond构造体持有一把锁,这个锁用来做协程间的同步操作;notify是一个唤醒队列,所有调用过Wait办法的协程都会被退出到这个唤醒队列中
Wait函数
func (c *Cond) Wait() { c.checker.check() t := runtime_notifyListAdd(&c.notify) c.L.Unlock() runtime_notifyListWait(&c.notify, t) c.L.Lock()}
首先,以后协程会被退出到notifyList中,之后会解锁L,这是因为以便让别的协程能够获取到锁,做同样的操作(lock, wait, unlock)中的lock这一步;之后runtime_notifyListWait
会阻塞,即所有调用过Wait函数的协程都会阻塞在这个中央;
之后若该协程被唤醒,那么runtime_notifyListWait
会返回,此时又回从新给L加锁,而后第一个抢占到这个返回操作的协程,它的Wait操作真正返回了;这个协程的Wait函数内部,可能后续还会做一些须要互斥的操作,在这个协程做完所有操作后,依照规定它会调用Unlock解锁,这时别的协程的Wait函数能力真正的返回,做后续操作;
对于唤醒函数就比较简单了:
func (c *Cond) Signal() { c.checker.check() runtime_notifyListNotifyOne(&c.notify)}func (c *Cond) Broadcast() { c.checker.check() runtime_notifyListNotifyAll(&c.notify)}
从队列中唤醒即可