python 并发编程 阻塞IO模型原理解析

 更新时间:2019年08月20日 10:57:39   作者:minger_lcm  
这篇文章主要介绍了python 并发编程 阻塞IO模型原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

阻塞IO(blocking IO)

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

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

等着对方把数据放到自己操作系统内存

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

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

这就是阻塞IO

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

网络编程都是从listen\(\)、send\(\)、recv\(\) 等接口开始的,
使用这些接口可以很方便的构建服务器/客户机的模型。然而大部分的socket接口都是阻塞型的。如下图

ps:

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

服务端:

from socket import *
server = socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',8000))
server.listen(5)
while True:
  print("starting...")
  conn,addr = server.accept()
  print(addr)
  while True:
    try:
      data = conn.recv(1024)
      if not data:break
      conn.send(data.upper())
    except ConnectionResetError:
      break
server.close()

客户端

from socket import *
client = socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8000))
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()

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

一个简单的解决方案:

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

该方案的问题是 :

开启多进程或都线程的方式,在遇到要同时响应成百上千路的连接请求,则无论多线程还是多进程都会严重占据系统资源,
降低系统对外界响应效率,而且线程与进程本身也更容易进入假死状态。
随着客户端数量增多,无限制的开线程,开销非常大
不能解决阻塞IO问题 ,解决思路:起多线程

改进方案:

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

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

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

并根据响应规模调整“池”的大小。

线程池应该随着规模数调大,但是调大线程池,要在机器可承受范围之内。不能把线程池无限调大,这样相当于无限开线程一样,

多线程还是要用在规模比较小的情况

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

总结:

始综没有解决单线程遇到IO问题,单线程遇到IO,就阻塞,用的是阻塞IO模型。

阻塞IO模型就是遇到IO阻塞不处理,就在原地等着。

应该:

监测单线程IO,遇到IO了,这个线程不要阻塞。直接切换到另外一个线程运行,这样单线程效率就非常高了。

要解决的问题是:

单线程IO问题

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • python cv2截取不规则区域图片实例

    python cv2截取不规则区域图片实例

    今天小编就为大家分享一篇python cv2截取不规则区域图片实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • Python flask与fastapi性能测试方法介绍

    Python flask与fastapi性能测试方法介绍

    这篇文章主要介绍了Python flask与fastapi性能测试方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • 详解python中xlrd包的安装与处理Excel表格

    详解python中xlrd包的安装与处理Excel表格

    python处理Excel常用到的模块是xlrd。使用xlrd可以非常方便的处理Excel文档,下面这篇文章将给大家详细介绍python中包xlrd的安装与利用xlrd处理Excel表格的方法,有需要的朋友们可以参考学习,下面来一起看看吧。
    2016-12-12
  • python 判断三个数字中的最大值实例代码

    python 判断三个数字中的最大值实例代码

    这篇文章主要介绍了python 判断三个数字中的最大值,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-07-07
  • OpenCV 绘制同心圆的示例代码

    OpenCV 绘制同心圆的示例代码

    本文主要介绍了OpenCV 绘制同心圆的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • pandas对指定列进行填充的方法

    pandas对指定列进行填充的方法

    下面小编就为大家分享一篇pandas对指定列进行填充的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • PyCharm汉化安装及永久激活详细教程(靠谱)

    PyCharm汉化安装及永久激活详细教程(靠谱)

    开始你的Python,这里有靠谱的Pycharm汉化安装详细教程,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • python实现n个数中选出m个数的方法

    python实现n个数中选出m个数的方法

    今天小编就为大家分享一篇python实现n个数中选出m个数的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-11-11
  • python 实现对文件夹内的文件排序编号

    python 实现对文件夹内的文件排序编号

    下面小编就为大家分享一篇python 实现对文件夹内的文件排序编号,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • 如何使用Python OpenCV提取物体轮廓详解

    如何使用Python OpenCV提取物体轮廓详解

    图像的轮廓检测不论是机器视觉还是其他方面都有较大作用,下面这篇文章主要给大家介绍了关于如何使用Python OpenCV提取物体轮廓的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05

最新评论