第四十次总结:IO模型了解

cpu运行程序

程序

main.py

它没有运行起来的时候,它只是一个文件,只会占用硬盘的空间。并不会占用内存空间。也不会消耗cpu。

 

进程

当main.py跑起来(运行起来)

会占用内存,存变量,存数据,存运行相关的一些内容

还会占用cpu

 

对于单核cpu要处理多个应用程序时

相当于是多个程序所在的进程,在抢占cpu的执行权

多线程本质并不是并发的

并不同时在执行多个线程

 

并不能够真正的同时利用到多个核来处理任务

 

 

任务分类

io型,(python的强项)

i,input,输入

o, output, 输出

 

 

计算密集型

[i for i in range(10000000)]

解决办法

如何让python能够实现多核功能

多进程可以用到多核的。真正执行的还是进程中的主线程。

多进程中开多任务(1,多线程方式。 2,协程方式)

多进程+协程

 

 

tcp

客户端》服务端

粘包现象

 

s.send()

s.send()

time.sleep(1)

s.send()

 

c.recv()

c.recv()

 

内核空间

 

qq音乐,一首歌,放三分钟

钉钉收消息,只需0.1秒钟

 

 

cpu在进行切换的进候,有两个标准

1,时间轮循。 有一个时间值n,0.2秒,3分钟 0.2秒----》1分钟解

2,闲置切换。 当一个进程一个临时小任务处理完后,马上切换,不会久留

 

 

并行:理想状态,一个cpu对一个进程

并发:常规状态,一个cpu对n个进程。

 

4个cpu,双核的

8个cpu 16个进程

 

1个cpu ,单核 16

 

 

GIL全局解释锁

 

 

io

IO在计算机中指Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。

比如你打开浏览器,访问新浪首页,浏览器这个程序就需要通过网络IO获取新浪的网页。浏览器首先会发送数据给新浪服务器,告诉它我想要首页的HTML,这个动作是往外发数据,叫Output,随后新浪服务器把网页发过来,这个动作是从外面接收数据,叫Input。所以,通常,程序完成IO操作会有Input和Output两个数据流。当然也有只用一个的情况,比如,从磁盘读取文件到内存,就只有Input操作,反过来,把数据写到磁盘文件里,就只是一个Output操作。

IO编程中,Stream(流)是一个很重要的概念,可以把流想象成一个水管,数据就是水管里的水,但是只能单向流动。Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是数据从内存流到外面去。对于浏览网页来说,浏览器和新浪服务器之间至少需要建立两根水管,才可以既能发数据,又能收数据。

由于CPU和内存的速度远远高于外设的速度,所以,在IO编程中,就存在速度严重不匹配的问题。举个例子来说,比如要把100M的数据写入磁盘,CPU输出100M的数据只需要0.01秒,可是磁盘要接收这100M数据可能需要10秒,怎么办呢?有两种办法:

第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行,这种模式称为同步IO;

另一种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,于是,后续代码可以立刻接着执行,这种模式称为异步IO。

同步和异步的区别就在于是否等待IO执行的结果。好比你去麦当劳点餐,你说“来个汉堡”,服务员告诉你,对不起,汉堡要现做,需要等5分钟,于是你站在收银台前面等了5分钟,拿到汉堡再去逛商场,这是同步IO。

你说“来个汉堡”,服务员告诉你,汉堡需要等5分钟,你可以先去逛商场,等做好了,我们再通知你,这样你可以立刻去干别的事情(逛商场),这是异步IO。

很明显,使用异步IO来编写程序性能会远远高于同步IO,但是异步IO的缺点是编程模型复杂。想想看,你得知道什么时候通知你“汉堡做好了”,而通知你的方法也各不相同。如果是服务员跑过来找到你,这是回调模式,如果服务员发短信通知你,你就得不停地检查手机,这是轮询模式。总之,异步IO的复杂度远远高于同步IO。

 

 

阻塞io

用户态向内核态发起数据的请求

只有内核态有数据的情况下

才会返回数据

否则会一直卡住

 

非阻塞io

 

设置套接字为非阻塞模式

tcp套接字.setblocking(False)

后果

accept()

recv()

不再阻塞

有数据,就获取

无数据,就报错

 

 

服务端代码

import socket
import time
# 服务端套接字
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
​
# 绑定端口
sk.bind(("",8899))
​
# 设置监听套接字
sk.listen(3)
​
sk.setblocking(False)
​
while True:
    # 等待连接
    try:
        print('等待用户连接....')
        conn, addr= sk.accept()
    except:
        print('当前没有用户,过会再来看看')
        time.sleep(5)
    else:
        print('有客户连接了', addr)

 

io多路复用

什么是I/O多路复用

关于什么是I/O多路复用,在知乎上有个很好的回答,可以参考罗志宇前辈的回答

  这里记录一下自己的理解。我认为要理解这个术语得从两方面去出发,一是:多路是个什么概念?二是:复用的什么东西?先说第一个问题。多路指的是多条独立的i/o流,i/o流可以这么理解:读是一条流(称之为读流,比如输入流),写是一条流(称之为写流,比如输出流),异常也是一条流(称之为异常流),每条流用一个文件描述符来表示,同一个文件描述符可以同时表示读流和写流。再来看第二个方面,复用的是什么东西?复用的是线程,复用线程来跟踪每路io的状态,然后用一个线程就可以处理所有的io。

当然,不提什么I/O多路复用也能在一个线程就处理完所有的io流,用个while循环挨个处理一次不就解决了嘛?那为什么还要提出这个技术呢?原因就是刚才我们想的方法(轮询)效率太低了,资源利用率也不高。试想一下,如果某个io被设置成了阻塞io,那么其他的io将被卡死,也就浪费掉了其他的io资源。另一方面,假设所有io被设置成非阻塞,那cpu一天到晚也不用干别的事了,就在这不停的问,现在可以进行io操作了吗,直到有一个设备准备好环境才能进行io,也就是在设备准备io环境的这一段时间,cpu是没必要瞎问的,问了也没结果。

随后硬件发展起来了,有了多核的概念,也就有了多线程。这个时候可以这样做,来一条io我开一个线程,这样的话再也不用轮询了。然而,管理线程是要耗费系统资源的,程序员也开始头疼了,线程之间的交互是十分麻烦的。这样一来程序的复杂性蹭蹭蹭地往上涨,io效率是可能提高了,但是软件的开发效率却可能减低了。

所以也就有了I/O多路复用这一技术。简单来说,就是一个线程追踪多条io流(读,写,异常),但不使用轮询,而是由设备本身告知程序哪条流可用了,这样一来就解放了cpu,也充分利用io资源,下文主要讲解如何实现这一技术,linux下这一技术有三个实现,select,poll,epoll。今天主要记录自己对select的理解,从接口到原理再到实现。

 

 

r, w, e= select.select([sk],[],[])   

参数1

是一个列表 【sk, conn1, conn2,conn2】

功能

哪些套接字有请求的变化

会把需要监控的套接字放在参数1的列表中

 

 

异步IO

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值