软件组件之间,函数之间的调用分为:同步调用,函数指针形式的同步回调,异步调用。前面两种很简单无需多言,这里只探索下异步调用。
自定义的异步事件回调机制:
可以在自己的应用程序中,先注册事件和事件对应的回调函数(回调函数可以是函数指针法,虚函数方法的方式);自己程序中每帧检测事件是否发生或者条件是否满足,满足的时候就进入回调函数。如果这样的检查是在同一个线程中那么就是同步的延迟调用,如果是在子线程中就是异步调用,但是这样的性能还是比较差的,除非不得以,否则还是用OS的异步回调机制性能高。
OS层面的异步事件回调机制:
linux下的异步回调机制:
1.异步事件的注册:会在内核里面产生一个事件放置到事件队列(属于内核事件或者线程/进程事件队列,加入事件select,poll是O(n)算法效率,epoll是O(1)算法效率因为使用了mmap不需要从用户空间拷贝到内核空间,其它事件类似);该事件会记录当前线程/进程标识号,关联的内核文件设备驱动事件,还可能需要一个回调函数。
2.异步事件的检测:检查时候会查询内核中的线程/进程事件队列(select,poll是O(n)算法效率,epoll是O(1)算法效率应该使用了数组类型的数据结构存储映射关系,其它事件类似 );阻塞线程/进程如果存在满足事件那么马上返回结果,如果不满足将会挂起当前线程/进程,释放CPU占用,提高性能。 非阻塞的线程/进程条件满足返回结果,条件不满足那么返回非阻塞的信息,可以继续做其它事情。
3.异步事件的回调驱动:文件设备驱动程序内有读写队列,当读写队列资源变为可读or写的时候(驱动程序可以周期性的检测读写队列而是计算机周期检测,非周期内驱动程序也可以挂起睡眠, 驱动程序也可以因为OS事件而唤醒,事件完成产生回调),将通过文件驱动资源和事件线程的关系(select poll o(n)算法效率内遍历事件队列,epoll O(1)时间算法效率,其它机制类似),唤醒在等待该资源的线程/进程继续执行(或发出事件通知到内核事件队列/线程进程的事件队列中)。唤醒后会再次判断文件设备条件是否满足因为非独占的资源可能被其它线程/进程获取了; 阻塞条件下成功了马上执行异步回调,返回中断现场继续执行程序逻辑,非阻塞条件下线程执行到此处检查事件队列将会成功从而产生异步调用,返回当时中断现场执行条件满足的后续逻辑,unity3d的coroutine也是这样的原理。
windows下的异步回调机制:
回调事件注册和异步事件的驱动都和linux下类似。
只是检查异步事件的消息, windows内核有一个事件队列,内核也为当前的用户线程创建事件队列,当内核驱动触发事件(周期触发或OS通知触发)时,消息会被分发到内核事件队列,分发到当前线程事件队列;当前线程需要一个消息循环不断的获取消息,当然也提供了阻塞模式和非阻塞模式的检测消息(getmessage没有获得去到消息会阻塞挂起当前线程,peekmessage没有获取到消息返回FALSE不会阻塞挂起当前线程),接收到了消息要进行分发处理。
如图:
为了更清楚地说明这个问题,我们参看图1:
相关文章:
http://blog.csdn.net/zmxiangde_88/article/details/7963884
http://blog.chinaunix.net/uid-26851094-id-3175832.html
http://blog.csdn.net/a725sasa/article/details/12225685
http://www.ibm.com/developerworks/cn/linux/l-callback/
http://www.cppblog.com/mzty/archive/2006/11/24/15619.html
http://www.vckbase.com/index.php/wv/1593
http://downloadt.advantech.com/ProductFile/Downloadfile3/EI-3BTKK/FAQ-10.pdf