当遇到一个协程写,其它协程处于等待读取的时候场景的时候,需要使用标准库中的sync.cond
才能优雅的解决。
package main
import (
"log"
"net/http"
"sync"
)
type Notifier struct {
m map[string]string
cond *sync.Cond
}
func NewNotifier(m map[string]string) *Notifier {
return &Notifier{
m: m,
cond: sync.NewCond(&sync.Mutex{}),
}
}
func listen(index int, name string, notifier *Notifier) {
v := notifier.m[name]
for {
notifier.cond.L.Lock()
for v == notifier.m[name] {
notifier.cond.Wait()
}
v = notifier.m[name]
log.Printf("index:%d received %s changed:%s", index, name, v)
notifier.cond.L.Unlock()
}
}
func broadcast(name string, value string, notifier *Notifier) {
notifier.cond.L.Lock()
notifier.m[name] = value
notifier.cond.L.Unlock()
notifier.cond.Broadcast()
}
func main() {
m := map[string]string{}
name := "time"
n := NewNotifier(m)
for i := -1; i < 3; i++ {
i := i
go func() {
listen(i, name, n)
}()
}
http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
time := request.URL.Query().Get("time")
broadcast(name, time, n)
})
http.ListenAndServe(":8090", nil)
}
当通过浏览器访问http://localhost:8090/test?time=33
时候,会触发listen
写入协程,然后会通过broadcast
来通过其它等待的协程。
通过更换time
的值,可以看到,我们期望的是当time
的值相比之前发生变化的时候,才能看到日志输出。