go version go1.13.4 linux/amd64
用于同步多个协程间的条件状态
比如在消费者-生产者模型中,只有在队列中有资源时,消费者才可以消费,只有在队列还能够放入资源时,生产者才可以放入资源。消费者协程和生产者协程之间需要同步队列的状态,这里的状态同步可以用sync.Cond来实现。
源码分析
// Wait atomically unlocks c.L and suspends execution
// of the calling goroutine. After later resuming execution,
// Wait locks c.L before returning. Unlike in other systems,
// Wait cannot return unless awoken by Broadcast or Signal.
// Wait方法释放锁,并阻塞协程执行。满足条件解除阻塞后,当前协程需要获得锁然后Wait方法返回。
//
// Because c.L is not locked when Wait first resumes, the caller
// typically cannot assume that the condition is true when
// Wait returns. Instead, the caller should Wait in a loop:
// 由于解除阻塞后,当前协程不一定能马上获得锁,因此返回后需要再次检查条件,所以通常
// 使用循环。
// c.L.Lock()
// for !condition() {
// c.Wait()
// }
// ... make use of condition ...
// c.L.Unlock()
//
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock() // 释放锁
runtime_notifyListWait(&c.notify, t) // 等待满足条件,解除阻塞
c.L.Lock() // 获取锁
}
// Signal wakes one goroutine waiting on c, if there is any.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Signal() {
c.checker.check()
runtime_notifyListNotifyOne(&c.notify)
}
// Broadcast wakes all goroutines waiting on c.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Broadcast() {
c.checker.check()
runtime_notifyListNotifyAll(&c.notify)
}
示例
这里用container/list简单模拟队列
这里只是简单用Cond实现生产者-消费者模型,实际上生产者-消费者模型由channel实现更加方便合理。
package main
import (
"container/list"
"fmt"
"math/rand"
"sync"
"time"
)
var (
lock = new(sync.Mutex)
cond = sync.NewCond(lock)
queue = NewQueue(5)
done = make(chan bool)
)
func main() {
for i := 0; i < 3; i++ {
go func(i int) {
for {
lock.Lock()
for queue.IsEmpty() {
cond.Wait()
}
fmt.Printf("消费者[%d]:%s\n", i, queue.Take().Data)
cond.Signal()
lock.Unlock()
}
}(i)
}
for i := 0; i < 5; i++ {
go func(i int) {
for {
lock.Lock()
for queue.IsFull() {
cond.Wait()
}
fmt.Printf("生产者[%d]\n", i)
queue.Put(&Product{Data: "hello, world"})
cond.Signal()
lock.Unlock()
r := rand.Int31n(3)
time.Sleep(time.Duration(r) * time.Second)
}
}(i)
}
<-done
}
type Product struct {
Data string
}
type Queue struct {
l *list.List
Cap int
}
func NewQueue(capacity int) *Queue {
return &Queue{l: list.New(), Cap: capacity}
}
func (q *Queue) Put(p *Product) {
q.l.PushBack(p)
}
func (q *Queue) Take() *Product {
t := q.l.Front()
defer q.l.Remove(t)
return t.Value.(*Product)
}
func (q *Queue) IsEmpty() bool {
return q.l.Len() == 0
}
func (q *Queue) IsFull() bool {
return q.l.Len() == q.Cap
}
这是一种通用的编程模式:
获取锁
while(条件不满足){
释放锁
线程/协程阻塞等待,直到收到条件满足的通知
获取锁
}
临界区操作
释放锁
java中可以采用synchronized、wait、notify和notifyAll实现,见synchronized