1. 怎么去通知阻塞协程继续运行
如果一个协程走到某个逻辑后,需要在某种条件达成后才能继续往下走,这怎么实现呢。对于单个协程,golang中的channel完全可以实现协程的通信。
如下,是使用channel,通知阻塞的协程继续运行
func main() {
s := &signal{
reRUn: make(chan struct{}, 1),
}
go func() {
time.Sleep(2 * time.Second)
s.signalReRun()
}()
go func(s *signal) {
//阻塞
<- s.reRUn
fmt.Println("goroutine start!")
}(s)
time.Sleep(5 * time.Second)
}
//通知协程继续执行
func (s *signal) signalReRun() {
select {
case s.reRUn <- struct{}{}:
fmt.Println("notify wait goroutine!")
default:
fmt.Println("default")
// The Channel is already full, so a reconnection attempt will occur.
}
}
但如果是多个协程同时等待某个条件呢,怎么实现呢?golang中提供了sync.cond即条件变量来完成这件事。
2. 条件变量
NewCond方法: 创建一个sync.cond对象,参数是实现了Locker接口的对象,即每个条件变量都关联一个锁,可以是mutex或者RWMutex。
// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {
return &Cond{L: l}
}
Locker
// A Locker represents an object that can be locked and unlocked.
type Locker interface {
Lock()
Unlock()
}
Signal方法:唤醒单个协程
func (c *Cond) Signal() {
c.checker.check()
runtime_notifyListNotifyOne(&c.notify)
}
Broadcast方法:唤醒所有等待的协程
func (c *Cond) Broadcast() {
c.checker.check()
runtime_notifyListNotifyAll(&c.notify)
}
wait方法: 调用wait会自动释放锁,并挂起所在的goroutine,携程阻塞于此,并等待siganal或者Broadcast 唤醒重新拿到锁继续执行。
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock()
runtime_notifyListWait(&c.notify, t)
c.L.Lock()
}
例子:
//主逻辑doWorker
func doWorker() {
cond := sync.NewCond(&sync.Mutex{})
for i := 0; i < 3; i++ {
go runWorker(cond, i)
}
time.Sleep(2*time.Second)
cond.Signal()
time.Sleep(2*time.Second)
cond.Broadcast()
}
//工作逻辑,在协程运行,等到条件变量唤醒
func runWorker(cond *sync.Cond, i int) {
cond.L.Lock()
cond.Wait()
cond.L.Unlock()
}