目录

go实现Event事件模型修正

描述

之前文章中实现了event事件模型,实际使用中发现了几个问题

  1. 并发会出现情况send先执行, 此时还没有waiter。将wait拆分成addWaiter和wait()

  2. 不能使用缓冲通道。会出现send完成,wait还未收到的情况。

用法修正

e.Reset()
waiter := e.AddWaiter()
waiter := e.Wait(waiter, xxx)
e.Send(xxx)

事件实现

package common

import (
	"context"
	"errors"
	"go.uber.org/zap"
	"time"
)

var NOT_USED = struct{}{}

func NewEvent() *Event {
	e := &Event{
		log: zap.S(),
	}
	e.Reset()
	return e
}

type Event struct{
	//监听者
	waiters  []chan interface{}
	//结果
	result interface{}
	//上下文控制
	ctxBg 	context.Context
	ctxCancel context.CancelFunc
	//日志
	log *zap.SugaredLogger
}


func (e *Event) AddWaiter() *chan interface{}{
	//等待者
	resultChan := make(chan interface{}, 0)
	e.waiters = append(e.waiters, resultChan)
	return &resultChan
}

//等待结果
func (e *Event) Wait(waiter *chan interface{}, timeout time.Duration) (interface{}, error){
	if e.result ==NOT_USED{
		ctx, cancel := context.WithTimeout(e.ctxBg, time.Second*timeout)
		defer cancel()

		//等待
		select{
		case result := <- *waiter:
			return result, nil
		case <- ctx.Done():
			if ctx.Err() == context.Canceled{
				return nil, nil
			}
			return nil, errors.New("event wait timeout")
		}
		return nil, nil
	}else{
		return e.result, nil
	}
}

//发送结果
func (e *Event) Send(result interface{}) error{
	//防止发送多次
	if e.result !=NOT_USED{
		return errors.New("Event is used")
	}

	ctx, cancel := context.WithTimeout(e.ctxBg, time.Second*3)
	defer cancel()

	for _, resultChan := range e.waiters{
		select{
		case resultChan <- result:
		case <- ctx.Done():
			e.log.Warnf("Event.Send %p resultChan=%d, result=%v", e, len(resultChan), result)
		}
	}
    e.result = result
	return nil
}

//重置
func (e *Event) Reset(){
	if e.ctxBg !=nil{
		e.ctxCancel()
	}

	e.ctxBg, e.ctxCancel = context.WithCancel(context.Background())
	e.waiters = nil
	e.result = NOT_USED
}

佛說大乘無量壽莊嚴清淨平等覺經pdf 净土大经科注2014-doc 此生必看的科学实验-水知道答案 印光大师十念法(胡小林主讲第1集)