python并发编程:阻塞IO

摘要:
阻塞IO在Linux中,默认情况下所有套接字都是阻塞的。典型的读取操作过程如下:当用户进程调用recvfrom系统调用时,内核启动IO的第一阶段:准备数据。然而,大多数套接字接口都是阻塞的。

阻塞IO(blocking  IO)

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

python并发编程:阻塞IO第1张

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

而在用户进程这边,整个进程会被阻塞,当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存
然后kernel返回结果,用户进程才解除block的状态,重新运行起来

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

几乎所有的程序员第一次接触到的网络编程都是从listen(),send(),recv()等接口开

始的,使用这些接口可以很方便的构建服务器/客户机的模型。然而大部分的socket接口都是

阻塞型的。如下图

ps:
所谓阻塞型接口是指系统调用(一般是IO接口)不返回调用结果并让当前线程一直阻塞

只有当该系统调用获得结果或者超时出错时才返回。

  

python并发编程:阻塞IO第2张

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

一个简单的解决方案:

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

  该方案的问题是:

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

  改进方案:

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

  改进后方案其实也存在着问题:

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

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

练习:

服务端:

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()

  

  

免责声明:文章转载自《python并发编程:阻塞IO》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇jquery遍历数组、对象一个简单的项目介绍流程下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android开发 -- Bootloader

本文转载自:http://blog.csdn.net/jmq_0000/article/details/7378348 LK是什么            LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代码  ,little kernel 是小内核小操作系统。           ...

Python安装官方whl包、tar.gz包、zip包

python依赖包下载官方地址:https://pypi.org/ 一、whl包安装   pip3 install XlsxWriter-1.0.5-py2.py3-none-any.whl  二、tar.gz包安装   安装tar.gz,命令格式:tar  -zxvf   压缩文件名.tar.gz解压后,cd文件 ->./configure  -&...

解读Python内存管理机制

转自:http://developer.51cto.com/art/201007/213585.htm 内存管理,对于Python这样的动态语言,是至关重要的一部分,它在很大程度上甚至决定了Python的执行效率,因为在Python的运行中,会创建和销毁大量的对象,这些都涉及到内存的管理。 内存管理,对于Python这样的动态语言,是至关重要的一部分,它在...

使用PyCharm进行远程开发和调试

http://blog.csdn.net/ll641058431/article/details/53049453 使用PyCharm进行远程开发和调试 你是否经常要在Windows 7或MAC OS X上面开发Python或Web应用程序,但是它们最后需要在linux上面来运行呢? 我们经常会碰到开发时没有问题但是到了正式的Linux环境下面却出现问题。...

socket.io建立长连接

   socket.io是基于node.js,在命令行里输入npm socket.io下载模块,用node.js搭建后台 示例代码,客户端 1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8"> 5...

pip install 默认安装路径修改

一、使用命令查看pip默认安装目录 python -m site 这里的USER_BASE和USER_SITE其实就是默认的启用Python通过pip自动下载的脚本和依赖安装包的基础路径。 接着使用命令python -m site -help,便会看到如下:  以上说明了,路径的配置是在我们安装目录下的/root/anaconda3/lib/pytho...