python并发编程之IO模型

时间:2022-08-28 18:00:53

一,IO模型-----为深入了解IO模型,同步,异步,阻塞,非阻塞。

同步(synchronous)IO和异步(asynchronous)IO,阻塞(blocking)IO和非阻塞(non-blocking)IO

1,等待数据准备------waiting for the data to be ready

2,将数据从内核拷贝到进程中------Copying the data from the kernel to the process

二,阻塞IO(blocking IO)

默认情况下socket都是blocking

                                       python并发编程之IO模型

blocking IO的特点就是:在IO自行的两个阶段(等待数据和拷贝数据两个阶段)都被block了

ps:所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞,只有当该系统调用获得结果或者超时出错时才返回

 python并发编程之IO模型

出特别指定,所有的IO接口(包括socket接口)都是阻塞型的。
解决IO接口存在的问题

1,在服务器端使用多线程(或多进程),多线程(或多进程)的目的是让每个连接都拥有独立的线程(或进程),这样任何一个连接的阻塞都不会影响其他的连接

存在问题:

开启多进程或多线程的方式,在遇到同时响应成百上千路的连接请求,则无论多线程还是多进程都会严重占据系统资源,降低系统对外界响应效率,而且线程与进程本身也容易进入假死状态

改进方案:

很多程序员可能会考虑使用线程池/进程池,线程池:指在减少创建和销毁线程的频率,其维持一定合理数量的线程,并让空闲的线程重新承担执行任务。连接池维持连接和缓存池,尽量重用已有的连接,减少创建和关闭连接的频率,这两种计数都可以很好地降低系统开销,都被广泛应用的很多大型系统,如websphere,tomcat和各种数据库等。

改进后的存在问题

线程池和连接池技术也只是在一定程度上缓解了频繁调用IO接口带来的资源占用。而所谓‘池’始终有其上限,当请求大大超过限时,‘池’构成的系统对外界的响应并不比没有吃的时候效果好多少,所以使用‘池’必须考虑其面临的响应规模,并根据响应规模调整‘池’的大小

     ***对应上例中的锁面临的可能同时出现同时上千万次客户端请求,‘线程池’/‘进程池’或许可以缓解部分压力,但是不能解决所有问题,总之,多线程模型可以方便高效的解决小规模的服务请求,但面对大规模的服务请求,多线程模型也会遇到瓶颈,可以用非阻塞接口来尝试解决这个问题。

三,非阻塞IO(non-blocking IO)

                         python并发编程之IO模型


在非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有

/*服务端

from socket import *
import time
s
=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,
1)
s.bind((
'127.0.0.1',8080))
s.listen(
5)
s.setblocking(False) #设置socket的接口为非阻塞
conn_l
=[]
while True:
try:
conn,addr
=s.accept()
print(addr)
conn_l.append(conn)
except Exception:
for conn in conn_l:
try:
data
=conn.recv(1024)
conn.send(data.upper())
except Exception:
continue

/*客户端

from socket import *
c
=socket(AF_INET,SOL_SOCKET)
c.connect((
'127.0.0.1',8080))

while True:
msg
=input('>>:').strip()
if not msg:continue
c.send(msg.encode(
'utf-8'))
data
=c.recv(1024)
print(data.decode(
'utf-8'))

优点:能够在等待任务完成的时间里干其他活(包括提交其他任务,也就是‘后台’也可以由多个任务在‘同时’执行)
缺点:循环调用recv是大量占用CPU,任务完成响应的延迟增大了。

在上方案中recv()更多的是起到检测‘操作完成’的作用

四,多路复用IO(IO multiplexing)

                                  python并发编程之IO模型


1,如果处理的连接不是很高的话,使用select/epoll的web server不一定比使用multi-threading+blocking IO的web server性能更好,可能延迟更大,select/epoll的优势并不是对于单个链接能处理的更快,而是在于能处理更多的连接

2,在多路复用模块中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process起始就是一直被block的,只不过process是被select这个函数block,而不是被socket IO给block。

结论:select的优势在于处理多个连接,不适用于单个连接。

 /*/*/*服务端

from socket import *
import
select
server
=socket(AF_INET,SOCK_STREAM)
server.bind((
'127.0.0.1',8085))
server.listen(
5)
server.setblocking(False)
print(
'starting...')
conn_l
=[]
del_l
=[]
while True:
try:
print(conn_l)
conn,addr
=server.accept()
conn_l.append(conn)
except BlockingIOError:
for conn in conn_l:
try:
data
=conn.recv(1024)
conn.send(data.upper())
except BlockingIOError:
pass
except BlockingIOError:
del_l.append(conn)
for obj in del_l:
obj.close()
conn_l.remove(obj)

continue

/*/*/*客户端

from socket import *
client
=socket(AF_INET,SOCK_STREAM)
client.connect((
'127.0.0.1',8085))


while True:
msg
=input('>>>:').strip()
if not msg:continue
client.send(msg.encode(
'utf-8'))
data
=client.recv(1024)
print(data.decode(
'utf-8'))


五,异步IO(Asynchronous I/O)

                        

                                       python并发编程之IO模型


用户进程发起read操作之后,立刻就可以开始做其他事,一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

 六,IO模型的分析比较(各个IO的model图的比较)

 python并发编程之IO模型