Python 第三方模块之 APScheduler - 定时任务
注解:selectors 模块是在 select 模块原型的基础上进行高级且高效的 I/O 复用。推荐用户改用 selectors 模块,除非用户希望对 OS 级的函数原型进行精确控制。
0、背景
对于初学 Socket 的人来说都不太爱用 Select 写程序,大部分人习惯写诸如connect、accept、recv 或 recvfrom 这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
可以使用 Select 完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
1、select 模块
- select 模块提供了对
select()
和poll()
函数的访问,这两个函数在大多数操作系统中是可用的。- 在 Linux2.5+ 上可用
epoll()
。 - 在 Solaris 及其衍生版本上可用
devpoll()
。 - 在大多数 BSD 上可用
kqueue()
。 - 在 Windows 上,本模块仅适用于套接字;在其他操作系统上,本模块也适用于其他文件类型(特别地,在Unix上也适用于管道)。
- 在 Linux2.5+ 上可用
- select 模块不能用于常规文件,不能检测出(自上次读取文件后)文件是否有新数据写入。
select模块内容:
select模块的函数 | 描述 |
---|---|
select.devpoll() |
返回一个 /dev/po11 轮询对象(仅支持 Solaris 及其衍生版本) |
select.epol1() |
返回一个 edge poll对象(仅支持 Linux 2.5.44 或更高版本) |
select.poll() |
返回一个 po11 对象(部分操作系统不支持) |
select.kqueue() |
返回一个内核队列对象(仅支持 FreeBSD) |
select.kevent() |
返回一个内核事件对象(仅支持 FreeBSD) |
select.select() |
返回值是三个列表,可以监视读、写、异常事件。当这些事件中的任何一个发生时,select函数会返回。Unixselect()系统调用接口 |
select.PIPE BUF() |
与管道相关 |
在使用 select 模块时,需要注意以下几点:
- 最好使用非阻塞的 socket,以避免程序在等待 socket 数据时被阻塞,从而可以处理其它 socket 数据。
- select 函数可能会有一些性能问题,当需要同时监听大量的文件描述符时,可能会导致 CPU 占用过高,所以使用时需要注意调优。
2、select.select()
1、select.select()
函数说明
1 |
|
当我们调用 select()
时:
- 上下文切换转换为内核态
- 将fd从用户空间复制到内核空间
- 内核遍历所有fd,查看其对应事件是否发生
- 如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历
- 返回遍历后的fd
- 将fd从内核空间复制到用户空间
2、select.select()
示例
示例:服务端
1 |
|
示例:客户端
- 可以开多个客户端同时连接服务端,每个客户端都可以与服务端进行独立的通信,互不影响。
1 |
|
2、select.epoll()
1、select.epoll()
函数说明
1 |
|
1、epoll 的 eventmask 事件含义
常量 | 含义 |
---|---|
EPOLLIN | 可读,状态符为1 |
EPOLLOUT | 可写,状态符为4 |
EPOLLPRI | 紧急数据读取 |
EPOLLERR | 在关联的文件描述符上有错误情况发生,状态符为8 |
EPOLLHUP | 关联的文件描述符已挂起 |
EPOLLET | 设置触发方式为边缘触发,默认为水平触发 |
EPOLLONESHOT | 设置 one-shot 模式。触发一次事件后,该描述符会在轮询对象内部被禁用。 |
EPOLLEXCLUSIVE | 当已关联的描述符发生事件时,仅唤醒一个 epo11 对象。默认(如果未设置此标志)是唤醒所有轮询该描述符的 epol1 对象。3.6新版功能:增加了EPOLLEXCLUSIVE。仅支持 Linux Kernel 4.5或更高版本。 |
EPOLLRDHUP | 流套接字的对侧关闭了连接或关闭了写入到一半的连接。 |
EPOLLRDNORH | 等同于EPOLLIN |
EPOLLRDBAND | 可以读职优先数据带。 |
EPOLLTRNORM | 等同于 EPOLLOUT |
EPOLLTRBAND | 可以写入优先级数据。 |
EPOLLMSG | 忽略 |
2、epoll 的方法
epol1的方法 | 描述 |
---|---|
epoll.close() |
关闭用于控制 epo11 对象的文件描述符。 |
epoll.closed() |
如果epo11对象已关闭,则返回True。 |
epoll.fileno() |
返回文件描述符对应的数字,该描述符用于控制epo11对象。 |
epoll.fromfd(fd) |
根据给定的文件描述符创建epo11对象。 |
epoll.register(fdl,eventmask]) |
在epo11对象中注册一个文件描述符。 |
epoll.modify(fd,eventmask) |
修改一个已注册的文件描述符。 |
epoll.unregister(fd) |
从epo11对象中删除一个已注册的文件描述符。 |
epol1.poll(timeout=None,maxevents=-1) |
等待事件发生,timeout 是浮点数,单位为秒。 |
3、水平触发(LT)和边缘触发(ET)
- LT(level triggered)
- 是缺省的工作方式(即epoll的默认工作方式),支持block和no-block socket。
- 只要满足条件,就触发一个事件。(只要有数据没有被获取完,内核就一直通知)
- 在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.
- 优点:当进行socket通信的时候,保证了数据的完整输出,进行IO操作的时候,如果还有数据,就会一直的通知你。
- 缺点:由于只要还有数据,内核就会不停的从内核空间转到用户空间,所有占用了大量内核资源,试想一下当有大量数据到来的时候,每次读取一个字节,这样就会不停的进行切换。内核资源的浪费严重。效率来讲也是很低的。
- ET(edge-triggered)
- 是高速工作方式,只支持no-block socket。
- 每当状态变化时,触发一个事件(内核只通知一次)
- 在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).
- 优点:每次内核只会通知一次,大大减少了内核资源的浪费,提高效率。
- 缺点:不能保证数据的完整。不能及时的取出所有的数据。
- 应用场景: 处理大数据。使用non-block模式的socket。
2、select.epoll()
示例
示例:服务端(必须运行在Linux2.5+)
1 |
|
示例:客户端
1 |
|
Reference
Python 第三方模块之 APScheduler - 定时任务
https://flepeng.github.io/021-Python-31-Python-标准库-10-网络相关-Python-内置模块之-select-IO-多路复用/