Go语言sync package提供了条件变量(condition variable)类型:
type Cond struct {
// L is held while observing or changing the condition
L Locker
// contains filtered or unexported fields
}
type Cond
func NewCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()
type Locker
type Locker interface {
Lock()
Unlock()
}
A Locker represents an object that can be locked and unlocked.
NewCond()函数输入参数是一个Locker接口类型,即实现了锁功能的变量。Broadcast()函数通知所有等待在condition variable的goroutine,而Signal()函数只会通知其中的一个goroutine。Wait()会让goroutine阻塞在condition variable,等待条件成立。通常的做法是:
c.L.Lock()
for !condition() {
c.Wait()
}
... make use of condition ...
c.L.Unlock()
进入Wait()函数会解锁,离开Wait()函数会重新加锁。由于在“解锁->收到通知->重新加锁”这段逻辑中间有可能另一个同样wait()的goroutine抢先一步改变了条件,导致当前goroutine的条件不再成立,所以这块要使用循环检测。参考下例:
package main
import (
"sync"
"time"
"fmt"
)
func main() {
c := sync.NewCond(&sync.Mutex{})
var num int
for i := 1; i <= 2; i++ {
go func(id int) {
fmt.Println("Enter Thread ID:", id)
c.L.Lock()
for num != 1 {
fmt.Println("Enter loop: Thread ID:", id)
c.Wait()
fmt.Println("Exit loop: Thread ID:", id)
}
num++
c.L.Unlock()
fmt.Println("Exit Thread ID:", id)
}(i)
}
time.Sleep(time.Second)
fmt.Println("Sleep 1 second")
num++
c.Broadcast()
time.Sleep(time.Second)
fmt.Println("Program exit")
}
一次执行结果如下:
Enter Thread ID: 2
Enter loop: Thread ID: 2
Enter Thread ID: 1
Enter loop: Thread ID: 1
Sleep 1 second
Exit loop: Thread ID: 2
Exit Thread ID: 2
Exit loop: Thread ID: 1
Enter loop: Thread ID: 1
Program exit
从上面例子可以看出由于goroutine 2改变了条件,导致goroutine 1重新进入循环,即多个goroutine阻塞在一个condition variable上存在着竞争的关系。