人工智能(PythonNet)—— IO操作之阻塞与非阻塞(select、poll和epoll)


一、IO操作

        IO:即Input/Output,凡是在内存中存在数据交换的操作都可以认为是IO操作。IO密集型程序:程序执行中执行大量的IO操作,而只有较少的cpu运算;消耗计算机资源较少,运行时间长。注:CPU密集型程序(计算密集型): 程序运行中需要大量的cpu运算,IO操作较少;消耗cpu资源多,运行速度快。

        举例:
                内存和磁盘交互       文件读写(read、write)
                内存和终端交互       输入打印(input、print)
                内存和网路交互       发送接收(send、recv) 

        IO操作的分类 :阻塞IO、非阻塞IO、IO多路复用、事件IO、异步IO

二、阻塞IO和非阻塞IO

1、阻塞IO

        阻塞IO是IO操作的默认形态,也是效率最低的一种IO操作。

        阻塞IO形成原因:因为等待某种条件达成才能继续运行,eg:accept、recv、input等;处理IO事件的时候耗时较长也会产生阻塞,eg:文件的读写过程,网络数据的传输过程等。

2、非阻塞IO :

        非阻塞IO通常是通过修改IO事件的属性,使其变为非阻塞状态,即避免条件阻塞的情况。

        非阻塞IO往往和循环搭配使用,这样可以不断执行部分需要执行的代码,也不影响对阻塞条件的判断;虽需要消耗更多cpu但是一定程度上提高了IO效率

3、设置套接字为非阻塞

        通过设置套接字为非阻塞使其成为非阻塞程序。       
            s.setblocking()
                功能 : 设置套接字的阻塞状态

                参数 : 设置为False则套接字调用函数为非阻塞

        通过设置超时检测使其成为非阻塞程序,即将原本阻塞的IO设置一个最长阻塞等待时间,在规定时间内如果达到条件则正常执行,如果时间到仍未达到条件则结束阻塞。
            s.settimeout(sec)
                功能 : 设置套接字超时时间
                参数 : 设置的时间

4、示例

        a、setblocking(false)
from socket import * 
from time import sleep,ctime

s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置s是非阻塞状态
s.setblocking(False)
while True:
    print("waiting for connect....")
    try:
        connfd,addr = s.accept()
    except BlockingIOError:
        sleep(2)
        print(ctime())
        continue

    print("connect from",addr)
    # recv变为非阻塞
    # connfd.setblocking(False)
    while True:
        data = connfd.recv(1024)
        if not data:
            break   
        print(data.decode())
        connfd.send('来,确认下眼神'.encode())
    connfd.close()
s.close()
        b、超时检测
from socket import * 
from time import sleep,ctime 

s = socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
#设置超时等待时间 5秒
s.settimeout(5)

while True:
    print("Waiting for connect...")
    try:
        connfd,addr = s.accept()
    except timeout:
        print("time out .....")
        sleep(2)
        print(ctime())
        continue

    print("Connect from",addr)

    while True:
        data = connfd.recv(1024).decode()
        if not data:
            break
        print(data)
        connfd.sendall(ctime().encode())
    connfd.close()

s.close()

三、IO多路复用

        IO多路复用指同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。此时形成多个IO事件都可以操作的现象,不必逐个等待执行。

        IO多路复用处理IO的过程中不应有死循环出现,使一个客户端长期占有服务端; IO多路复用是一种并发行为,但是是单进程程序,有较高的IO执行效率。

        IO多路复用函数有select、poll和epoll等函数,需要引入select模块(import select)。select函数适用于用于windows、linux和unix;poll和epoll函数适用于用于linux和unix。

        select、poll和epoll的 区别:

                1. select可以很好支持windows;

                2. epoll比select和poll效率高,select和poll差不多;

                3. epoll提供了更多的触发方式(epoll支持边缘触发  select  poll只支持水平触发)。

1、select函数

            r, w, x = select(rlist, wlist, xlist[, timeout])
                功能: 监控IO事件,阻塞等待IO事件发生
                参数:   rlist         列表  存放被动等待处理的IO事件
                              wlist        列表  存放需要主动处理的IO
                              xlist         列表  存放如果发生异常需要处理的IO
                              timeout   数字  超时监测 默认一直阻塞
                返回值 : r    列表   rlist中准备就绪的IO
                                w   列表   wlist中准备就绪的IO
                                x    列表   xlist中准备就绪的IO

2、poll函数

                from select  import  poll

        a、创建poll对象

                p = poll()

        b、添加关注的IO

                poll的IO 事件分类:
                        POLLIN  POLLOUT  POLLERR  POLLHUP  POLLPRI  POLLVAL
                        rlist           wlist            xlist       断开连接     紧急处理  无效

                p.register(s,POLLIN | POLLERR)

                p.unregister(s)    # 取消对IO的关注

        c、进行监控

                events = p.poll()
                    功能 : 监控关注的IO,阻塞等待IO发生
                    返回值 : events是一个列表,列表中每个元素为一个元组,代表准备就绪需要处理的IO。
                                    events --》 [(     fileno,            event),(),()]
                                                          就绪IO的fileno   哪个事件就绪

                因为要获取IO对象调用函数---》通过fileno得到对象
                实施方法 : 建立比照字典 {s.fileno():s}

        d、处理IO

3、epoll函数

                使用方法 : 与poll基本相同
                    生成对象使用epoll() 而不是poll() ,但是监控还是poll()
                    register注册IO事件事件类型改为epoll事件类型

四、示例

1、poll示例

from socket import *
from select import *

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",8888))
s.listen(5)

#创建POLL对象
p = poll()

#建立通过fileno查找IO对象的字典
fdmap = {s.fileno():s}

#注册关注IO事件
p.register(s,POLLIN | POLLERR)

while True:
    #监控关注的IO
    events = p.poll()

    for fd,event in events:
        if fd == s.fileno():
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            #注册新的关注IO
            p.register(c,POLLIN | POLLERR)
            #维护字典
            fdmap[c.fileno()] = c 
        elif event & POLLIN:
            data = fdmap[fd].recv(1024)
            if not data:
                #客户端退出,取消关注,维护字典
                p.unregister(fd)
                fdmap[fd].close()
                del fdmap[fd]
            else:
                print(data.decode())
                fdmap[fd].send('收到了'.encode())

2、epoll示例

from socket import *
from select import *

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",8888))
s.listen(5)

#创建EPOLL对象
p = epoll()

#建立通过fileno查找IO对象的字典
fdmap = {s.fileno():s}

#注册关注IO事件
p.register(s,EPOLLIN | EPOLLERR)

while True:
    #监控关注的IO
    events = p.poll()

    for fd,event in events:
        if fd == s.fileno():
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            #注册新的关注IO
            p.register(c,EPOLLIN | EPOLLERR)
            #维护字典
            fdmap[c.fileno()] = c 
        elif event & POLLIN:
            data = fdmap[fd].recv(1024)
            if not data:
                #客户端退出,取消关注,维护字典
                p.unregister(fd)
                fdmap[fd].close()
                del fdmap[fd]
            else:
                print(data.decode())
                fdmap[fd].send('收到了'.encode())

五、附录:目录

        人工智能(PythonNet)—— 目录汇总

你可能感兴趣的:(人工智能,PythonNet)