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")
    }
}
posted @ 2023-03-01 12:29  qxcheng  阅读(64)  评论(0编辑  收藏  举报