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问题

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

相关文章

  • Numpy 中的矩阵求逆实例

    Numpy 中的矩阵求逆实例

    今天小编就为大家分享一篇Numpy 中的矩阵求逆实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • python文本数据处理学习笔记详解

    python文本数据处理学习笔记详解

    这篇文章主要为大家详细介绍了python文本数据处理学习笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06
  • Python手绘可视化工具cutecharts使用实例

    Python手绘可视化工具cutecharts使用实例

    这篇文章主要介绍了Python手绘可视化工具cutecharts使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 详解python之多进程和进程池(Processing库)

    详解python之多进程和进程池(Processing库)

    本篇文章主要介绍了详解python之多进程和进程池(Processing库),非常具有实用价值,需要的朋友可以参考下
    2017-06-06
  • Python-torch 之torch.clamp() 函数解析

    Python-torch 之torch.clamp() 函数解析

    torch.clamp()函数用于对输入张量进行截断操作,将张量中的每个元素限制在指定的范围内,这篇文章主要介绍了Python torch之torch.clamp()函数,需要的朋友可以参考下
    2023-05-05
  • Python matplotlib画图与中文设置操作实例分析

    Python matplotlib画图与中文设置操作实例分析

    这篇文章主要介绍了Python matplotlib画图与中文设置操作,结合实例形式分析了Python使用matplotlib进行图形绘制及中文设置相关操作技巧,需要的朋友可以参考下
    2019-04-04
  • python绘图坐横坐标轴显示方式

    python绘图坐横坐标轴显示方式

    这篇文章主要介绍了python绘图坐横坐标轴显示方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • python使用torch随机初始化参数

    python使用torch随机初始化参数

    这篇文章主要介绍了python使用torch随机初始化参数,文章围绕torch随机初始化参数的相关资料展开文章详细内容,具有一定的参考价值,需要的小伙伴可以参考一下,希望对你有所帮助
    2022-03-03
  • Python中寻找数据异常值的3种方法

    Python中寻找数据异常值的3种方法

    这篇文章主要介绍了Python中寻找数据异常值的3种方法,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • Django项目中动态设置静态文件路径的全过程

    Django项目中动态设置静态文件路径的全过程

    这篇文章主要给大家介绍了关于Django项目中动态设置静态文件路径的相关资料,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02

最新评论