python 并发编程:阻塞 IO

本贴最后更新于 1576 天前,其中的信息可能已经物是人非

IO的模型:

image.png

了解概念:

阻塞I/O:应用程序执行I/O操作后,如果没有获得响应,就会阻塞当 前线程,自然就不能执行其他任务

非阻塞I/O:应用程序执行I/O操作后,不会阻塞当前的线程,可以继续执 行其他的任务,随后再通过轮询或者事件通知的形式,获取调用的结果

同步I/O:应用程序执行I/O操作后,要一直等到整个I/O完成后,才能获 得I/O响应

异步I/O:应用程序执行I/O操作后,不用等待完成和完成后的响应,而是 继续执行就可以

阻塞IO(blocking IO)

在Linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:

image.png

当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的udp包),这个时候kernel就要等待足够的数据到来

而在用户进程这边,整个进程会被阻塞,当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存

然后kernel返回结果,用户进程才解除block的状态,重新运行起来

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

image.png

实际上,除非特别指定,几乎所有的IO接口(包括socket接口)都是阻塞型的。这给网络编程带来了一个很大的问题。如在调用recv(1024)的同时,线程将被阻塞,在此期间,线程将无法执行任何运算或响应任何的网络请求。

下面使用python代码来演示:

服务端:

from socket import *

from threading import Thread

def communicate(conn):

while True:

try:

    data = conn.recv(1024)

    if not data:

        break

    conn.send(data.upper())

except ConnectionResetError:

    break

conn.close()
server = socket(AF_INET, SOCK_STREAM)

server.bind(('127.0.0.1', 8080))

server.listen(5)

while True:

print('starting...')

conn,addr = server.accept()

print(addr)

t = Thread(target=communicate, args=(conn,))

t.start()
server.close()

客户端:

from socket import *

client = socket(AF_INET, SOCK_STREAM)

client.connect(('127.0.0.1', 8080))

while True:

msg = input("请输入数据:").strip()

if not msg:

continue

client.send(msg.encode('utf-8'))

data = client.recv(1024)

print(data.decode('utf-8'))
client.close()

打开多个客户端,进行发送消息,多发送几次就会出现堵塞的情况:

image.png

客户端发送的信息,服务器没有及时返回结果

  • Python
    105 引用 • 237 回帖 • 2 关注
1 操作
mike.liu 在 2020-08-27 23:11:39 更新了该帖
回帖
请输入回帖内容 ...