精华内容
下载资源
问答
  • C# Event/UnityEvent辨析

    万次阅读 多人点赞 2017-10-27 16:21:24
    Event作为C#语言特性的一部分,在.Net开发中具有比较重要的作用。当我们使用C#作为脚本语言编写Unity游戏时,也经常会需要使用Event解决各种问题。然而,相比于传统C#语言的Event,UnityEvent基于Unity引擎做了一些...

    前言

    Event作为C#语言特性的一部分,在.Net开发中具有比较重要的作用。当我们使用C#作为脚本语言编写Unity游戏时,也经常会需要使用Event解决各种问题。然而,相比于传统C#语言的Event,UnityEvent基于Unity引擎做了一些改变,并且更加适合我们的游戏开发。为了帮助读者深入理解UnityEvent,本文会从Delegate讲起,并逐步介绍C# Event 与UnityEvent的相似与不同。
    本文参考了Unity论坛以及其他前辈的文章和视频,想要进一步了解的可以自行查阅:

    一切的渊源——Delegate

    要理解Event是什么,首先必须得知道它们的前身——Delegate是啥,中文翻译即“委托”。用一句话让你理解Delegate的作用就是“Delegate是一个可以存放函数的容器”。众所周知,变量是程序在内存中为数据开辟的一块空间,面向对象语言中变量可以存放一个具体的数值,或者某个对象的引用。C#则在该基础上更进一步,使用Delegate的机制让存放“函数(Function)”成为可能。
    使用Delegate一般分为三步:

    1. 定义一种委托类型
    2. 声明一个该类型的委托函数
    3. 通过声明的委托调用函数执行相关的操作

    下面是在Unity中使用Delegate的一个实例:

    using UnityEngine;
    
    public class DelegateExample : MonoBehaviour {
        //Step1. 为Delegate定义一种函数原型
        public delegate void MyDelegate(int args);
        //Step2. 声明一个Delegate变量
        public MyDelegate myDelegate;
    
        private void Start()
        {
            //Step3. 引用Delegate变量实现相应的函数
            myDelegate = PrintNum;
            myDelegate(5);
    
            myDelegate = PrintDoubleNum;
            myDelegate(5);
        }
    
        public void PrintNum(int num)
        {
            print("Print number: " + num);
        }
    
        public void PrintDoubleNum(int num)
        {
            print("Print double number: " + num*2);
        }
    }

    运行上述代码,你会发现使用一个delegate变量让我们执行了两种函数实现。这就是Delegate的妙处所在, Delegate定义了一个用于存放相同函数原型(相同参数类型,相同返回值)的容器。因为他们的函数原型相等,所以向delegate传递一次参数,就可以让所有添加到delegate上的函数正常执行
    在上述例子中,我们第二次向myDelegate赋值时覆盖掉了第一次赋值的函数,所以第二次引用myDelegate只会调用PrintDoubleNum(int num)函数。实际上,delegate作为函数容器,并不仅仅只能容纳一个函数,而是可以同时被委任多个函数。例如,当你把上述代码中的第二次赋值改为 myDelegate += PrintDoubleNum; 就可以实现同时打印两条语句的效果。这种delegate一般被称为multicast delegate

    基于delegate实现的Event(C# Event)

    如果你基本理解了delegate是什么,那么理解event基本不需要花费什么时间,因为event就是在multicast delegate的基础上演变来的。关于Event,一个比较形象的比喻就是广播者和订阅者。把Event想象成一个视频作者,并且他还具有一大堆热情的粉丝,每天都在等待新视频的发布。为了在第一时间收看到新发布的视频,粉丝们大多会选择订阅视频作者(大多数视频网站的套路),这样作者更新时你就会收到一条即时消息。在程序的世界里,event可能不再是一个做视频的,毕竟做视频赚不到什么钱,但是他依然为喜爱他的观众(具有相同函数类型的函数)提供了订阅他的途径(即把自身加入到event的函数容器中),这样无论他有什么动向,都可以直接通知所有他知道的粉丝(调用event会立即引用所有函数容器中的函数)。当然,一千个观众心中就有一千个哈姆雷特,就如同真爱粉无论如何都会支持自己的偶像,而黑粉无时无刻不在带节奏一样,event只负责告诉每个函数什么时候被调用,这些函数到底干了什么,event并不关心。

    下面以代码的形式演示上述过程:

    Idol.cs

    using UnityEngine;
    
    public class Idol : MonoBehaviour {
        public delegate void IdolBehaviour(string behaviour);
        public static event IdolBehaviour IdolDoSomethingHandler;
    
        private void Start()
        {
            //Idol 决定搞事了, 如果他还有粉丝的话, 就必须全部都通知到
            if (IdolDoSomethingHandler != null)
            {
                IdolDoSomethingHandler("Idol give up writing.");
            }
        }
    }

    SubscriberA.cs

    using UnityEngine;
    
    public class SubscriberA : MonoBehaviour {
        /// <summary>
        /// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
        /// </summary>
        private void OnEnable()
        {
            //粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
            Idol.IdolDoSomethingHandler += LikeIdol;
        }
    
        /// <summary>
        /// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
        /// </summary>
        private void OnDisable()
        {
            Idol.IdolDoSomethingHandler -= LikeIdol;
        }
    
        /// <summary>
        /// 粉丝A是一个脑残粉
        /// </summary>
        /// <param name="idolAction"></param>
        public void LikeIdol(string idolAction)
        {
            print(idolAction + " I will support you forever!");
        }
    }
    

    SubscriberB.cs

    using UnityEngine;
    
    public class SubscriberB : MonoBehaviour {
        /// <summary>
        /// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
        /// </summary>
        private void OnEnable()
        {
            //粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
            Idol.IdolDoSomethingHandler += HateIdol;
        }
    
        /// <summary>
        /// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
        /// </summary>
        private void OnDisable()
        {
            Idol.IdolDoSomethingHandler -= HateIdol;
        }
    
        /// <summary>
        /// 粉丝B是一个无脑黑
        /// </summary>
        /// <param name="idolAction"></param>
        public void HateIdol(string idolAction)
        {
            print(idolAction + " I will hate you forever!");
        }
    }

    把他们分别绑定在一个GameObject上,并运行游戏,你就可以在Console上看到两种粉丝对爱豆发表的见解。这里有几点使用时的注意事项希望引起你的重视:

    1. 想让爱豆直接通知你TA的近况,你就必须在他发出消息前完成订阅。在本例中,虽然两个粉丝和爱豆位于不同的GameObject上,但是他们都提前订阅了Idol,所以Idol才能正确通知到他们(OnEnable()在执行顺序上优先于Start(),关于OnEnable/Start/Awake的辨析,欢迎阅读我的博客: Awake/Start/OnEnable 辨析
    2. 在本例中两个粉丝均采用了调用静态字段IdolDoSomethingHandler的方法实现订阅,实际上你也可以为每个粉丝添加一个public Idol idol;然后在Editor上直接绑定。(这点是Unity特有的)
    3. 偶像并不关心他的粉丝对自己的行为作出何种反映。甚至在他发出消息时,除了确认一下自己还没有过气(IdolDoSomethingHandler != null)之外,对粉丝的行为不会有任何了解。(在降低耦合性loose decoupling 的同时,隐藏了事件函数的实现细节)
    4. 你并不需要担心偶像受不受得了同时给那么多粉丝发邮件,因为一般有经纪人代办(误)。细心的人可能会发现我们在声明event delegate时并没有给它分配内存,使用时直接赋值或添加即可。

    UnityEvent

    经过上一节的解释,你应该对Event是什么,怎么用有了一个大概的体会,那么这一节我们就接着讲一讲Unity在Event的基础上进行的改良,即UnityEvent。Event设计之初并不会想到应用于Unity游戏开发,所以它的弊端就在于纯代码编程,没有通过使用Unity Editor提高工作效率。而UnityEvent就可以看做是发挥Editor作用的正确改良。还记得上一节中粉丝是怎么订阅的嘛?你必须在每个粉丝对象中访问Idol的IdolDoSomethingHandler,然后把自己将采取的行动添加上去。这样有两个坏处——其一就是你必须时刻提防订阅的时机,假如不小心在Idol发动态之后才订阅,那你就永远收不到那条动态了。其二就是不方便管理,想要查看订阅偶像的所有粉丝,我们就得查找项目中所有IdolDoSomethingHandler的引用,然后再把每个粉丝的文件打开,可以说是非常麻烦了。
    为了避免上述的缺点,UnityEvent使用Serializable让用户可以在Editor中直接绑定所有粉丝的调用,即一目了然又不用担心把握不准订阅的时机。

    话不多说,直接上代码:

    Idol.cs

    using UnityEngine;
    using UnityEngine.Events;
    
    //使用Serializable序列化IdolEvent, 否则无法在Editor中显示
    [System.Serializable]
    public class IdolEvent : UnityEvent<string> {
    
    }
    
    public class Idol : MonoBehaviour {
        //public delegate void IdolBehaviour(string behaviour);
        //public event IdolBehaviour IdolDoSomethingHandler;
        public IdolEvent idolEvent;
    
        private void Start()
        {
            //Idol 决定搞事了, 如果他还有粉丝的话, 就必须全部都通知到
            if (idolEvent == null)
            {
                idolEvent = new IdolEvent();
            }
            idolEvent.Invoke("Idol give up writing.");
        }
    }

    SubscriberA.cs

    using UnityEngine;
    
    public class SubscriberA : MonoBehaviour {
        /// <summary>
        /// 粉丝A是一个脑残粉
        /// </summary>
        /// <param name="idolAction"></param>
        public void LikeIdol(string idolAction)
        {
            print(idolAction + " I will support you forever!");
        }
    }
    

    SubscriberB.cs

    using UnityEngine;
    
    public class SubscriberB : MonoBehaviour {
        /// <summary>
        /// 粉丝B是一个无脑黑
        /// </summary>
        /// <param name="idolAction"></param>
        public void HateIdol(string idolAction)
        {
            print(idolAction + " I will hate you forever!");
        }
    }

    把上面三个脚本绑定到三个GameObject上,但是不要着急立刻运行游戏,因为我们还没有让两个粉丝实现订阅。和使用Event时不同,UnityEvent在序列化后可以在Editor上显示,并且可以让我们在Editor阶段就设置好需要执行的函数。选中Idol所在的GameObject,然后就可以在Inspector中设置IdolEvent可以引用的函数。设置完成后应该如图所示。

    设置IdolEvent

    此时再运行游戏,你会得到和使用基于delegate的Event时相同的效果。

    除此之外,UnityEvent依然提供和C# Event 类似的运行时绑定的功能,不过不同的是,UnityEvent是一个对象,向其绑定函数是通过AddListener()方法实现的。以SubscriberB为例,我们可以在代码中实现同等效果的绑定:

    SubscriberB.cs

    using UnityEngine;
    
    public class SubscriberB : MonoBehaviour {
        public Idol myIdol;
    
        /// <summary>
        /// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
        /// </summary>
        private void OnEnable()
        {
            //粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
            myIdol.idolEvent.AddListener(HateIdol);
        }
    
        /// <summary>
        /// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
        /// </summary>
        private void OnDisable()
        {
            myIdol.idolEvent.RemoveListener(HateIdol);
        }
    
        /// <summary>
        /// 粉丝B是一个无脑黑
        /// </summary>
        /// <param name="idolAction"></param>
        public void HateIdol(string idolAction)
        {
            print(idolAction + " I will hate you forever!");
        }
    }

    由于UnityEvent是一个对象,所以自然可以允许我们通过继承实现自己的Event,实际上Unity中包括Button在内的许多UI组件的点击事件都是通过继承自UnityEvent来复写的。
    可访问性(public/private)决定了UnityEvent的默认值,当可访问性为public时,默认会为其分配空间(new UnityEvent());当可访问性为private时,默认UnityEvent为null,需要在Start()中为其分配内存。

    展开全文
  • 使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个 event_base 结构体持有一个事件集合,可以检测以确定哪个事件是激活的。 4.1.1 创建默认的event_base struct event_base *event_base_new...

    一.event_base

    (一) libevent简介与浅谈event_base

    在这里插入图片描述

    1. libevent实际上就是对底层select/poll/epoll等进行了封装,每个event_base都有一种“方法”,该“方法”是select、poll、epoll、kqueue、devpoll、evport、win32。
    2. 使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个 event_base 结构体持有一个事件集合,可以检测以确定哪个事件是激活的。
    3. event_base 相当于是一个底座,只要向底座上插入事件,然后不断的监控事件,等待事件发生调用回调函数即可

    (二) event_base的API

    1. 检查event_base后端方法(IO多路复用方法)

    函数声明 功能
    const char **event_get_supported_methods(void); 返回一个指针 ,指向 libevent 支持的IO多路方法名字数组,这个数组的最后一个元素是NULL
    const char *event_base_get_method(const struct event_base *base); 返回 event_base 正在使用的IO多路方法
    enum event_method_feature event_base_get_features(const struct event_base *base); 返回 event_base 支持的特征的比特掩码
    //libevent的版本
    printf("Starting Libevent %s. Available methods are:\n", event_get_version());
    
    //检查支持的IO多路方法
    const char **methods = event_get_supported_methods();
    for (int i=0; methods[i] != NULL; ++i) {
    	printf(" %s\n", methods[i]);
    }
    
    struct event_base *base = event_base_new();
    enum event_method_feature f;
    
    if (!base) 
    {
    	puts("Couldn't get an event_base!");
    } 
    else 
    {
    	//返回 event_base 正在使用的IO多路方法
    	printf("Using Libevent with backend method %s\n",event_base_get_method(base));
    	
    	//返回 event_base 支持的特征的比特掩码
    	f = event_base_get_features(base);
    	if ((f & EV_FEATURE_ET)) //支持边沿触发的后端
    		printf(" Edge-triggered events are supported.");
    	if ((f & EV_FEATURE_O1)) //添加、删除单个事件,或者确定哪个事件激活的操作是 O(1)复杂度的后端
    		printf(" O(1) event notification is supported.");
    	if ((f & EV_FEATURE_FDS)) //要求支持任意文件描述符,而不仅仅是套接字的后端
    		printf(" All FD types are supported.");
    }
    

    2. 创建事件根基event_base_new

    struct event_base *event_base_new(void);
    功能:函数会检测环境变量,返回一个event_base的指针,分配并且返回一个新的具有默认设置的 event_base。

    3. 释放事件根基event_base_free

    void event_base_free(struct event_base *base); //释放event_base
    注意:这个函数不会释放当前与 event_base 关联的任何事件,或者关闭它们的套接字 ,或者释放任何指针。应该手动的释放它们

    4. event_base和fork

    int event_reinit(struct event_base *base);
    因为不是所有的安插在event_base的事件在调用fork()之后都可以正常工作,所以,如果在使用fork()或者其他相关系统调用启动一个新的进程之后,要想在子进程中使用base变量,但是又想让该base变量是一个全新的没有安插事件的变量,就应该在子进程中对base调用event_reinit函数进行重新初始化。

    [伪代码]
    	struct event_base* base=event_base_new();
    	
    	//向event_base中安插事件
    	
    	if(fork()) // parent process
    	{
    		continue_runing_parent(base); 
    	}
    	else //child process
    	{
    		event_reinit(base); //重新初始化子进程从父进程继承下来的base
    		continue_runing_child(base);
    	}
    

    5. 设置event_base支持的优先级别个数

    int event_base_priority_init(struct event_base *base, int n_priorities);

    • 功能:给event_base设置共有n_priorities个优先级级别,以便于事件安插在event_base之前可以设置[0, n_priorities-1]级别的优先级
    • 返回值:成功时这个函数返回 0,失败时返回 -1。
    • 参数:
      • base是要修event_base
      • n_priorities是要支持的优先级数目,这个数目至少是 1 。每个新的事件可用的优先级将从 0 (最高) 到 n_priorities-1(最低)。常量 EVENT_MAX_PRIORITIES 表示 n_priorities 的上限。

    默认情况下,与 event_base 相关联的事件的优先级将默认被初始化为 n_priorities / 2


    二.事件循环 event_loop

    一旦创建好事件根基event_base,并且在根基上安插好事件之后,需要对事件循环监控(换句话说就是等待事件的到来,触发事件的回调函数),有两种方式可以达到上面描述的功能,即:event_base_dispatch和event_base_loop

    (一) 事件循环

    方式1:int event_base_dispatch(struct event_base *);

    方式2:int event_base_loop(struct event_base *base, int flags);

    • 参数flags
      • EVLOOP_ONCE:相当于epoll_wait阻塞方式&&只调用一次 ⇒ 当没有事件到来时,程序将一直阻塞在event_base_loop函数;直到有任意一个事件到来时,程序才不会阻塞在event_base_loop,将会继续向下执行。
      • EVLOOP_NONBLOCK:相当于epoll_wait非阻塞方式&&只调用一次 ⇒ 即使没有事件到来,程序也不会阻塞在event_base_loop
      • EVLOOP_NO_EXIT_ON_EMPTY:等价于event_base_dispatch ⇒ 将一直循环监控事件 ⇒ 直到没有已经注册的事件 || 调用了event_base_loopbreak()或 event_base_loopexit()为止

    (二) 终止事件循环

    event_base_dispatch函数退出的三种情况
    当所有的事件都active完毕,此时没有处于未决状态(正在被监听)的事件时
    调用event_base_loopexit(struct event_base*, const struct timeval*)函数
    调用event_base_loopbreak(struct event_base*)函数

    如果想在移除所有已注册的事件之前停止活动的事件循环,可以调用两个稍有不同的函数

    函数声明 功能与区别
    int event_base_loopexit(struct event_base *base, const struct timeval *tv); 让 event_base 在给定时间之后停止循环。如果 tv 参数为 NULL ,event_base 会立即停止循环,没有延时。如果 event_base 当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出
    int event_base_loopbreak(struct event_base *base); 让 event_base 立即退出循环(即使有其他正在执行任何激活事件的回调)

    (3)转储event_base的状态

    void event_base_dump_events(struct event_base *base, FILE *f);
    功能:将event_base上安插的事件情况,写入到绑定的文件中

    FILE *fp = fopen("event_base_stat.txt","a"); 
    event_base_dump_events(base, fp); //将base的状态,保存到文件中
    fclose(fp);
    

    三.事件event

    (一) 事件状态转换图

    在这里插入图片描述
    event的使用步骤

    在base上安装、监控event的流程
    struct event* ev=event_new(base,fd,what,cb,arg)创建事件,事件处于非未决/初始化状态
    event_add(ev,NULL); //使事件处于未决状态
    event_base_dispatch(base); //循环检测事件,事件发生时触发回调函数
    event_free(ev);释放事件
    event_base_free(base);释放event_base

    信号事件signalEvent、事件event的使用步骤对比
    可以看到:

    1. signalEvent和event的类型一样,都是struct event*
    2. 使用过程也完全一样
    3. 唯一的不同点,是在创建struct event*对象时不同,分别为evsignal_new/event_new
    4. 警告:不要在信号事件上设置超时,这可能是不被支持的。[待修正:真是这样的吗?]
    信号事件signalEvent 事件event
    struct event *signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); struct event* ev=event_new(base,fd,what,cb,arg)
    event_add(signal_event, NULL) event_add(ev,NULL);

    (二) 事件event相关API

    1.创建事件: event_new \ event_assign

    创建并初始化struct event类型的事件变量,根据创建的位置分为堆/栈

    创建位置 函数原型
    堆区 struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, void (*cb)(evutil_socket_t, short, void *), void *arg);
    栈区 int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
    event_new event_assign
    功能 试图在堆上分配和构造一个用于 base 的新的event 在栈上已经初始化好struct event类型的变量,使用event_assign函数对其进行初始化
    使用步骤 struct event * ev; struct event ev;
    ev=event_new(_,_,_,_,__); event_assign(&ev,_,_,_,_,__);
    警告 不要对已经在 event_base 中处于未决状态的事件调用 event_assign(如果已经初始化和成为未决的,需要调用event_assign之前需要调用event_del)

    参数介绍

    • fd:如果fd非负,表示被观察其读写事件的文件
    • cb/arg:事件激活时,调用的回调函数cb和参数arg
    • what:
      • EV_TIMEOUT 超时
      • EV_READ 可读
      • EV_WRITE 可写
      • EV_SIGNAL 用于实现信号检测,请看下面的 “构造信号事件”节
      • EV_PERSIST 事件是“持久的”
      • EV_ET 边沿触发事件

    [重点]关于事件持久性:EV_PERSIST

    (1)是否设置EV_PERSIST标志的本质区别:事件从active状态结束后,将变成 [ 未决状态 / 非未决状态 ] ?

    是否设置EV_PERSIST持久标志? 事件在进入active状态并且回调函数执行完毕后,事件状态从active状态变成什么状态?
    设置 未决状态(事件仍然被监听)
    未设置 非未决状态(事件已经不被监听)

    (2)分析程序,深刻的体会设置/不设置EV_PERSIST的区别

    输入任意字符,按回车后,代码的执行结果
    情况1:不设置EV_PERSIST持久标志 事件被触发后,事件状态进入非未决状态,此时没有被监听的事件 ==> 导致程序执行callback后,直接退出
    情况2:设置EV_PERSIST持久标志 事件被触发后,事件状态进入未决状态,此时事件仍然被监听;并且缓冲区的数据一直存在 ==> 导致程序一直执行callback函数,不断的打印横线
    void event_cb(evutil_socket_t fd, short what, void *arg)
    {
      printf("____________________\n");
    }
    
    int main()
    {
      struct event_base* base = event_base_new();
      
      int fd = 0;
      //情况1:不设置EV_PERSIST持久标志
      struct event* ev = event_new(base,fd,EV_TIMEOUT|EV_READ/*|EV_PERSIST*/,event_cb,NULL);
      //情况2:设置EV_PERSIST持久标志
      struct event* ev = event_new(base,fd,EV_TIMEOUT|EV_READ|EV_PERSIST,event_cb,NULL);
    
      event_add(ev,NULL); //使事件处于未决状态
    
      event_base_dispatch(base); //循环检测事件,直到没有要注册的事件或者调用exit/break函数
    
      event_free(ev); // 释放事件 void event_free(struct event *event);
      event_base_free(base);
      return 0;
    }
    

    2.event_add \ event_del

    功能:让事件从非未决状态/未决状态之间切换
    int event_add(struct event *ev, const struct timeval *tv);
    int event_del(struct event *ev);

    3. 带优先级的事件:event_priority_set

    int event_priority_set(struct event *event, int priority); //设置事件event的优先级priority,其中priority属于[0, n_priorities-1]

    设置事件优先级的步骤 相关API使用
    1. 创建event_base和event event_base_new()、event_new()
    2. 设置event_base支持的[优先级的数目n_priorities] event_base_priority_init(base, n_priorities);
    3. 设置event的优先级 event_priority_set(ev, 0);
    4. 将event安插到event_base上

    说明:如果不为事件event设置优先级,则默认的优先级等于 event_base 的优先级数目n_priorities除以2

    4. 手动激活事件: event_active

    void event_active(struct event *ev, int what, short ncalls);
    功能:使没被触发的事件,成为active状态
    重点

    1. 事件不需要已经处于未决状态:即,不调用event_add(ev)的事件ev,也能被event_active触发
    2. 激活事件也不会让它成为未决的激活事件也不会让它成为未决的:即,事件active后,还是保持未active之前的状态,并不是进入未决状态

    代码案例
    程序执行结果:程序执行一次event_cb函数后,直接退出
    分析原因:调用event_active函数后,事件ev并没有变成“未决状态”,导致此时没有待监控的事件,所以event_base_dispatch函数返回,程序退出

    void event_cb(evutil_socket_t fd, short what, void *arg)
    {
      printf("____________________\n");
    }
    
    int main()
    {
      struct event_base* base = event_base_new();
      
      int fd = 0;
      
      //EV_PERSIST永久监控EV_READ事件
      struct event* ev = event_new(base,fd,EV_TIMEOUT|EV_READ|EV_PERSIST,event_cb,NULL); 
    
      //event_add(ev,NULL);  //不需要通过event_add使事件ev处于未决状态
    
      event_active(ev,EV_READ,0); //触发事件后,事件并没进入未决状态
    
      event_base_dispatch(base);
      event_free(ev);
      event_base_free(base);
      return 0;
    }
    

    5. 一次触发事件event_base_once

    函数原型:int event_base_once(struct event_base *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);

    1. event_base_once()函数除了不支持EV_SIGNAL或EV_PERSIST外,它与event_new()函数相同
    2. 事件回调函数执行后,libevent内部会将会释放event结构
    3. event_base_once ()插入的事件不能删除/取消 或 手动激活
    展开全文
  • LTE测量事件主要有下面几种:Event A1、Event A2、Event A3、Event A4、Event A5、Event B1、Event B2。 原帖:https://wenda.so.com/q/1451887876725924 Event A1 (Serving becomes better than threshold):表示...

    LTE测量事件主要有下面几种:Event A1、Event A2、Event A3、Event A4、Event A5、Event B1、Event B2。

    原帖:https://wenda.so.com/q/1451887876725924

    Event A1 (Serving becomes better than threshold)
    :表示服务小区信号质量高于一定门限,满足此条件的事件被上报时,eNodeB停止异频/异系统测量;类似于UMTS里面的2F事件;


    Event A2 (Serving becomes worse than threshold)
    :表示服务小区信号质量低于一定门限,满足此条件的事件被上报时,eNodeB启动异频/异系统测量;类似于UMTS里面的2D事件;


    Event A3 (Neighbour becomes offset better than serving)
    :表示同频/异频邻区质量高于服务小区质量,满足此条件的事件被上报时,源eNodeB启动同频切换请求;一般来说是基于覆盖的切换


    Event A4 (Neighbour becomes better than threshold)
    :表示异频邻区质量高于一定门限量,满足此条件的事件被上报时,源eNodeB启动异频切换请求;一般来说是基于负荷均衡的切换


    Event A5 (Serving becomes worse than threshold1 and neighbour becomes better than threshold2)
    :表示服务小区质量低于一定门限并且邻区质量高于一定门限;类似于UMTS里的2B事件;


    Event B1 (Inter RAT neighbour becomes better than threshold)
    :表示异系统邻区质量高于一定门限,满足此条件事件被上报时,源eNodeB启动异系统切换请求;类似于UMTS里的3C事件;


    Event B2 (Serving becomes worse than threshold1 and inter RAT neighbour becomes better than threshold2)
    :表示服务小区质量低于一定门限并且异系统邻区质量高于一定门限,类似于UMTS里进行异系统切换的3A事件。

    转载于:https://www.cnblogs.com/mway/p/8559409.html

    展开全文
  • Event Manager and Event Listener

    千次阅读 2015-03-15 16:21:28
    在我见过的Event Managers事件管理器示例,将触发该事件的方法是在同一个class 作为事件管理器。喜欢这个:using UnityEngine; using System.Collections; public class EventManager : MonoBehaviour {
    

    我已经读完关于事件的文件,看着一对夫妇的教程,但是还有一些我仍然不握。在我见过的Event Managers事件管理器示例,将触发该事件的方法是在同一个class 作为事件管理器。喜欢这个:

    using UnityEngine;
     using System.Collections;
     
     public class EventManager : MonoBehaviour {
         
         public delegate void CheckpointHandler(int id);
         public static event CheckpointHandler checkpointReached;
     
         void OnGUI () {
         
             if( GUI.Button(new Rect(5,5,150,40),"Checkpoint")){
                 checkpointReached(555);
             }
             
         }
     
     }
    

    所以在这里,EventManager 不仅定义委托和事件,而且还创建一个按钮,将触发该事件。

    这是什么让我困惑。以为 EventManager 将什么也不做只是是定义的事件。然后你会解雇那些来自其他脚本的事件。有没有办法做到这一点吗?

    我不喜欢此示例设置,因为它似乎必须将"EventManager"脚本附加到每个可能可以触发事件的对象的方式。如果有多个对象,可以引发同一事件吗?我需要将相同的"EventManager"附加到每一个人吗?

    我想我可能会回答我自己的问题,但也许有人可以告诉我,是否这将执行成功与否。

    我成立了 EvenManager 中的事件触发器,作为公共方法,然后使用拖放的方法将 EventManager 置于其他类,使其方法可为任何想要触发的事件。所以我的设置如下所示 (所有这些都连接到独立游戏对象):

    using UnityEngine;
     using System.Collections;
     
     public class EventManager : MonoBehaviour {
     //Here I define the delegates, events and the triggers that can be called to fire the event.
         
         public delegate void CheckpointHandler(int id);
         public static event CheckpointHandler checkpointReached;
     
         
         public void hitCheckpoint (int id){
             if (checkpointReached != null)
                 checkpointReached(id);
         }
     }
     using UnityEngine;
     using System.Collections;
     
     public class EventTrigger : MonoBehaviour {
     /******
     In this class I can set up a method that will access the EventManager 
     (which has been dropped onto the em variable) 
     and fire the hitCheckpoint method, 
     which in turn sends the checkpointReached event.
     ******/
         
         
         public EventManager em;
     
         void OnGUI () {
         
             if( GUI.Button(new Rect(5,5,150,40),"Checkpoint    ")){
                 em.hitCheckpoint(555);
             }
             
         }
     }
    
     using UnityEngine;
     using System.Collections;
     
     public class EventListener : MonoBehaviour {
         //This script listens for the event and prints a message to the log.
         
         void OnEnable () {
             EventManager.checkpointReached += HandleEventManagercheckpointReached;
         }
         
         void OnDisable(){
             EventManager.checkpointReached -= HandleEventManagercheckpointReached;
         }
     
         void HandleEventManagercheckpointReached (int id)
         {
             Debug.Log ("Checkpoint reahed. ID is: " + id);
         }
     }
    








    展开全文
  • event.initEvent()

    千次阅读 2017-11-28 14:39:13
    event.initEvent()   标签:  javascript   it     DOM createEvent() 方法 定义和用法 初始化新事件对象的属性 语法 event.initEvent(eventType,...
  • event.target 和 event.currentTarget 的区别

    千次阅读 2020-10-28 09:33:16
    event.target This property of event objects is the object the event was dispatched on. It is different than event.currentTarget when the event handler is called in bubbling or capturing phase of the...
  • QT Event以及EventFilter事件处理是本文要介绍的内容,详细内容如下,先来看内容。EventEvent Filters: 1、手动发送事件流程: (1)构造自己的事件对象: QEvent*evt=newQEvent(QEvent::Close); (2)...
  • IROS2018 Event Camera

    万次阅读 2019-12-14 16:16:00
    Event Camera AsynchronousCornerDetectionandTrackingforEventCamerasinReal-Time, Ignacio Alzugaray and Margarita Chli [pdf] [video]
  • 1.event.stopPropagation()方法 这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开, 2.event.preventDefault()方法 ...
  • Java - The Event Model

    万次阅读 2019-11-07 10:47:50
    分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!...Aneventis a signal received by a program from the operating system as a result of some action taken by t...
  • pygame.event.Event

    千次阅读 2013-07-28 17:20:59
    查看所有的Event 只需要将event打印出来即可 import pygame,sys from pygame.locals import * pygame.init() pygame.display.set_mode((600,480)) while True: for event in pygame.event.get(): print(event)
  • 1、XID_EVENT当事务提交时,不论是statement还是row格式的binlog都会添加一个XID_EVENT作为事务的结束。该事件记录了该事务的ID。在mysql进行崩溃恢复时根据binlog中提交的情况来决定是否提交存储引擎中prepared状态...
  • 在Android的 onTouchEvent(MotionEvent event)方法中event.getX()、event.getY()与event.getRawX()、event.getRawY()其实表达的含义是有区别的,我们先来看图了解下:  红色的外框表示的是手机屏幕 蓝色外框表示...
  • PS:最近看到有很多地方用到UnityAction和UnityEvent的地方,自己也刚刚学习,总结一下 Action 与event Action也是基于委托,并且无返回值。可带参数Action&lt;T1,T2,T3,T4&gt;。通常大家可以用在订阅...
  • jquery里面对event.which 将 event.keyCode 和 event.charCode 标准化了 可是为什么还有好多人在jquery代码里面这样写 e.keyCode||e.which||e.charCode; 而不是直接使用e.which 难道是我理解错了吗??
  • javascript判断是否按回车键 ... onkeypress="enterHandler(event);"/>   function enterHandler(event) { //获取用户单击键盘的“键值” var keyCode = event.keyCode ? event.keyCode : event.which ? event.
  • Flink水涨船高:EventTime和WaterMark

    万次阅读 2020-03-04 17:08:11
    但是呢对于EventTime,拿我们Web网站的日志来说,EventTime即是日志中的时间戳,但是发送数据的情况不可能总是那么理想,到达Flink的顺序不可能刚好是时间戳的顺序,为了控制这种乱序的情况,引入了WaterMark,中文...
  • muduo之EventLoop

    千次阅读 2019-10-10 20:52:40
    EventLoop.cc就相当于一个reactor,多线程之间的函数调用(用eventfd唤醒),epoll处理,超时队列处理,对channel的处理。运行loop的进程被称为IO线程,EventLoop提供了一些API确保相应函数在IO线程中调用,确保没有用...
  • JS 中的 event?event:window.event什么意思?求详解。2013-04-16 00:01flying607 | 分类:JavaScript | 浏览813次function Test(event) { event = event ? event : window.event;} 请问:1,“onclick = "Test...
  • size="90" onkeypress="enterHandler(event);"/>   function enterHandler(event) { //获取用户单击键盘的“键值” var keyCode = event.keyCode ? event.keyCode  : event.which ?
  • [libevent]event,event_base结构体描述

    千次阅读 2015-04-16 23:28:20
    libevent的核心-event Libevent是基于事件驱动(event-driven)的,从名字也可以看到event是整个库的核心。event就是Reactor框架中的事件处理程序组件;它提供了函数接口,供Reactor在事件发生时调用,以执行相应的...
  • 解决Invalid handler for event “click“:问题

    万次阅读 热门讨论 2019-03-28 21:15:24
    解决Invalid handler for event "click":问题 上一篇文章介绍了如何搭建一个基于vue和ElementUi 的项目,当我在项目中添加 一个button按钮,并未这个按钮添加一个@click事件 <el-button type="primary" icon=...
  • event 和 window.event

    千次阅读 2011-10-09 16:42:15
    event 和 window.event 出处:http://hi.baidu.com/zzcc_8/blog/item/1c6e7a8f560765f3503d9200.html function test(event) { event = even
  • event_base

    千次阅读 2018-08-10 19:57:32
    event_base 介绍   一个event_base就是一个Reactor框架。我们在调用任何Libevent的函数前,我们都是需要先申请 event_base 结构体。对于一个event_base结构来说,它会保存一系列的event事件并且以轮训的方式去...
  • &lt;...gt; &lt; script language="... event = event || window.event; if(event.keyCode==13){ alert("你按了回车") } if(event.shiftKey==true) { ale...
  • event.shiftKey 语法:event.altKey 取值:true | false 1|0 说明: altKey属性为true表示事件发生时Alt键被按下并保持,为false则Alt键没有按下。  altKey属性可结合鼠标或键盘使用,多用于制作一些...
  • 我们知道观察者模式可以实现代码的解耦,而spring的event模型就是这种设计模式的极佳体现。一个事件包含:事件发布、监听、和事件源。在spring中我们可以通过ApplicationContext的publishEvent方法去发布事件;通过...
  • DOM事件类型、event事件对象,最详解析。

    万次阅读 多人点赞 2020-05-11 11:02:28
    目录事件对象(一)DOM中的事件对象⑴ 示例⑵ 所有事件都有的event对象的属性和方法① currentTarget | this | targer 的区别 事件对象 在触发DOM上的某个事件时,会产生一个事件对象event,这个事件对象包含着所有...
  • event.currentTarget 在事件冒泡阶段中的当前DOM元素 $("p").click(function(event) { alert( event.currentTarget === this ); // true }); event.data 当前执行的处理器被绑定的时候,包含可选的数据...
  • JS event事件

    千次阅读 2018-09-04 16:13:33
    event对象 当dom tree中某个事件被触发的时候,会自动产生一个用来描述事件所有的相关信息的对象,这个对象就是event(事件对象); 可通过window.event/event来获取。非IE还可以通过函数传参的形式来使用,一般...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 313,636
精华内容 125,454
关键字:

event