提高性能的多任务编程
可以用单线程-单进程-非堵塞并发的方法来实现多任务,
socket.setblocking(False) # 将套接字变为非堵塞
,会让accept在没有客户端到来之前和socket.recv()没有收到数据的时候从堵塞变为异常,从而我们可以让它抛出异常,继续执行下面的代码。
我们可以新建一个列表,当有客户端连接时,把accept返回的套接字添加到这个列表里面,然后遍历列表,查看每个套接字是否收到了数据,没有收到抛出异常,继续执行代码,从而可以以并发的方式实现单线程单进程完成同时对多个客户端的服务。
当列表里的元素越来越多时,这种方法(轮询)的效率会大大降低,所以引入了epoll ,用事件通知的方式来监听列表里的套接字
select轮询:
for循环套接字列表,实际上是把每一个套接字对象的描述符拷贝到内存中,然后查看是否接收到了数据,所以效率比较慢
epoll的实现原理:
epoll采用了内存映射技术,它会在内存中单独开辟出一个供系统和进程共用的一个空间,然后将对应的文件描述符注册到这个内存当中,当其中基于某个文件描述符读就绪时,通过基于事件的就绪通知方式,来激活对应的文件描述符
用epoll的方法就不用讲套接字设置成非堵塞模式了,因为poll()会默认堵塞,只有当有新客户端到来或者是有事件通知时,才会解堵塞
I/O多路复用:遇到I/O操作会自动执行其他代码
通过一种机制使单进程实现同时等待多个文件描述符,当其中任意一个描述符就绪时,epoll()函数就会返回。所以I/O复用本质上不是并发,因为任何时候都是单进程单线程处理,之所以能够提高效率,是因为select/epoll可以同时监测所有进来的socket,一旦有socket有数据读写,就会立刻返回给进程处理(是基于事件就绪通知的方式而不是轮询)
如何更好地实现高性能并发:
可以使用多线程/多进程和I/O复用的方式混合,I/O负责提高监测socket通知效率,收到请求后返回给进程池/线程池来执行逻辑代码
使用gevent来实现http服务器多任务时,不用使用joinall,遇到堵塞会自动执行