Go组件库总结之事件注册唤醒
本篇文章我们用Go实现一个自定义事件注册并等待唤醒的机制,其中涉及到的链表操作可以参考上一篇文章。文章参考自:https://github.com/brewlin/net-protocol
1.自定义唤醒事件
type EventMask uint16
const (
EventIn EventMask = 0x01
EventPri EventMask = 0x02
EventOut EventMask = 0x04
EventErr EventMask = 0x08
EventHUp EventMask = 0x10
EventNVal EventMask = 0x20
)
2.等待对象
type Entry struct {
Context interface{}
Callback EntryCallback
mask EventMask
ilist.Entry
}
3.等待对象被唤醒后的回调
type EntryCallback interface {
Callback(e *Entry)
}
4.EntryCallback接口的实现
Callback向等待对象的channel发送值以唤醒channel上的等待协程。
type channelCallback struct{}
func (*channelCallback) Callback(e *Entry) {
ch := e.Context.(chan struct{})
select {
case ch <- struct{}{}:
default:
}
}
5.初始化等待对象
这里的channel将会用于阻塞未被唤醒时的协程。
func NewChannelEntry(c chan struct{}) (Entry, chan struct{}) {
if c == nil {
c = make(chan struct{}, 1)
}
return Entry{Context: c, Callback: &channelCallback{}}, c
}
6.等待队列
type Queue struct {
list ilist.List
mu sync.RWMutex
}
7.事件注册和事件注销
// EventRegister 注册等待对象
func (q *Queue) EventRegister(e *Entry, mask EventMask) {
q.mu.Lock()
e.mask = mask
q.list.PushBack(e)
q.mu.Unlock()
}
// EventUnregister 注销等待对象
func (q *Queue) EventUnregister(e *Entry) {
q.mu.Lock()
q.list.Remove(e)
q.mu.Unlock()
}
8.事件唤醒
根据事件类型,通过回调函数向channel发送值以唤醒等待队列中的所有相关等待对象。
func (q *Queue) Notify(mask EventMask) {
q.mu.RLock()
for it := q.list.Front(); it != nil; it = it.Next() {
e := it.(*Entry)
if mask&e.mask != 0 {
e.Callback.Callback(e)
}
}
q.mu.RUnlock()
}
9.其他方法
// Events 返回已注册的事件类型
func (q *Queue) Events() EventMask {
ret := EventMask(0)
q.mu.RLock()
for it := q.list.Front(); it != nil; it = it.Next() {
e := it.(*Entry)
ret |= e.mask
}
q.mu.RUnlock()
return ret
}
// IsEmpty 等待队列是否为空
func (q *Queue) IsEmpty() bool {
q.mu.Lock()
defer q.mu.Unlock()
return q.list.Front() == nil
}
10.使用示例
func main() {
var wq waiter.Queue
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
wq.EventRegister(&waitEntry, waiter.EventIn)
defer wq.EventUnregister(&waitEntry)
go func() {
for {
time.Sleep(time.Second)
wq.Notify(waiter.EventIn)
}
}()
for {
<-notifyCh
fmt.Println("waked")
}
}