1.并发、并行、同步、异步、阻塞、非阻塞
并发:是指在同一个时间段内,有几个程序都处于启动运行到运行结束之间
并行:在同一个时间点上,有几个程序同时运行
同步:当一个同步操作发出去后,调用者一直等待返回消息结果,才能进行后续的操作 比如操作文件 打开文件 读取文件 都是同步操作
异步: 当一个异步操作发出去后,调用者不能立刻得到消息结果 创建线程 都是异步操作
阻塞:调用结果返回之前,当前线程会被挂起来,一直处于等待消息通知,不能执行其他业务
非阻塞:调用结果返回之前,该函数不会阻塞当前线程 而立刻返回
2.IO多路复用select、poll、epoll
select
它通过一个select()系统调用来监视多个文件描述符,当select()返回后,该数组中就绪的文件描述符会被内核修改标志位,使进程能够获得这些文件描述符,从 而进行后续的修改
缺点:单个进程能够监视的文件描述符的数量存在最大限制 一般1024
poll
本质跟select()没有区别 但是poll没有限制,使用的是链表存储
epoll
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的 值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符 在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描, 而poll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描 述符,当进程调用epoll_wait()时便得到通知。
epoll实列
1 import selectors,socket 2 def accept(sock, mask): 3 conn, addr = sock.accept() 4 print('accepted{}from{}'.format(conn,addr)) 5 conn.setblocking(False) 6 sel.register(conn,selectors.EVENT_READ, read) 7 8 def read(conn, mask): 9 data = conn.recv(1024) 10 if data: 11 print('echoing{}to{}'.format(repr(data),conn)) 12 else: 13 print('closing',conn) 14 sel.unregister(conn) 15 conn.close() 16 sel = selectors.DefaultSelector() 17 server = socket.socket() 18 server.bind(('localhost', 9999)) 19 server.listen(500) 20 server.setblocking(False) 21 sel.register(server, selectors.EVENT_READ, accept) #注册事件,只要来一个连接就调accept这个函数, 22 23 while True: 24 events = sel.select()#这个select,看起来是select,有可能调用的是epoll,看你操作系统是Windows的还是Linux的 25 #默认阻塞,有活动连接就返回活动连接列表 26 print('事件',events) 27 28 for key,mask in events: 29 callback = key.data 30 callable(key.fileobj, mask)
客户端
import socket,sys server_address = ('localhost', 9999) # 创建100个 TCP/IP socket实例 socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(100)] # 连接服务端 print('connecting to %s port %s' % server_address) for s in socks: s.connect(server_address) s.send(b'kehuduan') data = s.recv(1024) print('服务端回来的数据{}'.format(data)) if not data: print('连接失败') s.close()