精华内容
下载资源
问答
  • 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_newevent_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和eventevent_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.initEvent()

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

    event.initEvent()

     
    标签: 

    javascript

     

    it

     
     

    DOM createEvent() 方法

    定义和用法

    初始化新事件对象的属性

    语法

    event.initEvent(eventType,canBubble,cancelable)
    参数 描述
    eventType 字符串值。事件的类型。
    canBubble 事件是否起泡。
    cancelable 是否可以用 preventDefault() 方法取消事件。

    说明

    该方法将初始化 document_createEvent() 方法创建的合成 Event 对象的 type 属性、bubbles 属性和 cancelable 属性。只有在新创建的 Event 对象被 Document 对象或 Element 对象的 dispatchEvent() 方法分派之前,才能调用 Event.initEvent() 方法。

     

    定义和用法

    createEvent() 方法创建新的 Event 对象

    语法:

    createEvent(eventType)
    参数 描述
    eventType

    想获取的 Event 对象的事件模块名。

    关于有效的事件类型列表,请参阅“说明”部分。

    返回值

    返回新创建的 Event 对象,具有指定的类型。

    抛出

    如果实现支持需要的事件类型,该方法将抛出代码为 NOT_SUPPORTED_ERR 的 DOMException 异常

    说明

    该方法将创建一种新的事件类型,该类型由参数 eventType 指定。注意,该参数的值不是要创建的事件接口的名称,而是定义那个接口的 DOM 模块的名称。

    下表列出了 eventType 的合法值和每个值创建的事件接口:

    参数 事件接口 初始化方法
    HTMLEvents HTMLEvent iniEvent()
    MouseEvents MouseEvent iniMouseEvent()
    UIEvents UIEvent iniUIEvent()

    用该方法创建了 Event 对象以后,必须用上表中所示的初始化方法初始化对象。关于初始化方法的详细信息,请参阅 Event 对象参考


     

    XML DOM dispatchEvent() 方法

    定义和用法

    dispatchEvent() 方法给节点分派一个合成事件。

    语法:

    dispatchEvent(evt)
    参数 描述
    evt 必需。要分派的 Event 对象。

    返回值

    如果在事件传播过程中调用了 evt 的 preventDefault() 方法,则返回 false,否则返回 true。

    抛出

    如果 Event 对象 evt 没有被初始化,或者它的 type 属性为 null 或空串,该方法将抛出异常。


    定义和用法

    initEvent() 方法初始化新事件对象的属性。

    语法

    event.initEvent(eventType,canBubble,cancelable)
    参数 描述
    eventType 字符串值。事件的类型。
    canBubble 事件是否起泡。
    cancelable 是否可以用 preventDefault() 方法取消事件。

    说明

    该方法将初始化 document_createEvent() 方法 创建的合成 Event 对象的 type 属性、bubbles 属性和 cancelable 属性。 只有在新创建的 Event 对象被 Document 对象 或 Element 对象 的 dispatchEvent() 方法 分派之前,才能调用 Event.initEvent() 方法。

    event.initEvent()
    1.  if(document.all){  //ie下 
    2.         document.getElementByIdx_x("a3").click();  
    3.     }
    4.     else 
    5.         var evt document_createEvent("MouseEvents");  
    6.         evt.initEvent("click"truetrue);  
    7.         document.getElementByIdx_x("a3").dispatchEvent(evt);  
    8.     
    展开全文
  • 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)...
  • PS:最近看到有很多地方用到UnityAction和UnityEvent的地方,自己也刚刚学习,总结一下 Action 与event Action也是基于委托,并且无返回值。可带参数Action&lt;T1,T2,T3,T4&gt;。通常大家可以用在订阅...
  • 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)
  • 在Android的 onTouchEvent(MotionEvent event)方法中event.getX()、event.getY()与event.getRawX()、event.getRawY()其实表达的含义是有区别的,我们先来看图了解下:  红色的外框表示的是手机屏幕 蓝色外框表示...
  • javascript判断是否按回车键 ... onkeypress="enterHandler(event);"/>   function enterHandler(event) { //获取用户单击键盘的“键值” var keyCode = event.keyCode ? event.keyCode : event.which ? event.
  • 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...
  • 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在事件发生时调用,以执行相应的...
  • pygame之event模块

    千次阅读 2019-01-23 17:30:38
    event-用于与事件和队列进行交互的Pygame模块 pygame.event.pump—内部处理pygame事件处理程序 pygame.event.get—从队列中获取事件 pygame.event.poll—从队列中获取单个事件 pygame.event.wait—等待队列中的...
  • 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
  • keybd_event 使用方法

    万次阅读 多人点赞 2019-09-28 19:32:24
    Windows 提供了一个模拟键盘 API 函数 Keybd_event(),使用该函数可以相应的屏蔽键盘的动作。Keybd_event()函数能触发一个按键事件,也就是说会产生一个 WM_KEYDOWN 或 WM_KEYUP 消息。 该函数原型如下: ...
  • event_base

    千次阅读 2018-08-10 19:57:32
    event_base 介绍   一个event_base就是一个Reactor框架。我们在调用任何Libevent的函数前,我们都是需要先申请 event_base 结构体。对于一个event_base结构来说,它会保存一系列的event事件并且以轮训的方式去...
  • 使用event_base_loopbreak或event_base_loopexit无法让event_base_dispatch退出事件循环 原因及解决方案: 经过一天的折腾,发现是多线程环境下没有调用evthread_use_windows_threads或evthread_use_threads函数...
  • pygame.event 用于处理事件与事件队列的 Pygame 模块。 函数 pygame.event.pump()—让 Pygame 内部自动处理事件 pygame.event.get()—从队列中获取事件 pygame.event.poll()—从队列中获取一个事件 pygame....
  • 首先,event loop 就是一个普通 Python 对象,您可以通过 asyncio.new_event_loop() 创建无数个 event loop 对象。只不过,loop.run_xxx() 家族的函数都是阻塞的,比如 run_until_complete() 会等到给定的 coroutine...
  • @EventListener注解使用及源码解析

    万次阅读 多人点赞 2019-07-29 15:54:20
    一、简介 @EventListener是一种事件驱动编程在spring4.2的时候开始有的,早期可以实现ApplicationListener接口, 想了解下ApplicationListener的可以参考下这篇文章...内部实现原...
  • muduo之EventLoop

    千次阅读 2019-10-10 20:52:40
    EventLoop.cc就相当于一个reactor,多线程之间的函数调用(用eventfd唤醒),epoll处理,超时队列处理,对channel的处理。运行loop的进程被称为IO线程,EventLoop提供了一些API确保相应函数在IO线程中调用,确保没有用...
  • &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属性可结合鼠标或键盘使用,多用于制作一些...
  • JS Event对象详解

    千次阅读 2018-02-11 23:45:30
    JS Event对象详解 参考资料: js添加事件和移除事件:addEventListener()与removeEventListener() Event 对象 Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的...
  • Event 采集详细配置 目前,JDK 11 一共有136个 Event 采集配置。这里会比较详细的去看每一个Event,并说明基本应用,建议配置。如果 default.jfc 中没有打开或者需要修改的配置,会将配置文件代码发出来。 1. JFR ...
  • event.currentTarget 在事件冒泡阶段中的当前DOM元素 $("p").click(function(event) { alert( event.currentTarget === this ); // true }); event.data 当前执行的处理器被绑定的时候,包含可选的数据...
  • 在基于 Element-ui 写项目的时候...[Violation] Added non-passive event listener to a scroll-blocking 'mousewheel' event. Consider marking event handler as 'passive' to make the page more responsive. ...
  • IE中,event对象有srcElement属性,但是没有target属性。 firefox中,event对象有target属性,但是没有srcElement属性。 可以使用,var node = event.srcElement?event.srcElement:event.target; 或,var node =...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,456,686
精华内容 582,674
关键字:

event