IO模型

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
非阻塞IO
IO多路复用
异步IO

同步:提交一个任务之后,等待这个任务执行完毕
异步:只管提交任务,不等待这个任务执行完毕就可以做其他事情
阻塞:recv recvfrom accept
非阻塞:

IO发生时涉及的对象和步骤:对于一个network IO(以read举例),他会涉及到两个IO对象,一个是调用这个IO的process(or thread),另一个就是系统内核(kernet)。当一个read操作发生时,该操作会经历两个阶段:
(1)等待数据准备
(2)将数据从内核拷贝到进程中

阻塞IO
阻塞IO

非阻塞IO
非阻塞IO模型图
用非阻塞IO实现一个socket并发收发消息
sever端

import socket
sk = socket.socket()
sk.bind(('192.168.0.102', 8080))
sk.setblocking(False)      # 把socket当中所有需要阻塞的方法都改编成非阻塞
sk.listen(5)
conn_l = []             # 用来储存所有来请求server端的conn的链接
del_conn = []           # 用来储存所有与server端断开的conn链接
while True:
    try:
        conn, addr = sk.accept()  # 不阻塞,但没人链接会报错
        print('建立连接了', addr)
        conn_l.append(conn)
    except BlockingIOError:
        for conn in conn_l:
            try:
                msg = conn.recv(1024)    # 非阻塞,如果没有数据就报错
                if msg == b'':
                    del_conn.append(conn)
                    continue
                print(msg)
                conn.send(b'byebye')
            except BlockingIOError:pass
        for conn in del_conn:
            conn.close()
            conn_l.remove(conn)
        del_conn.clear()

client端

import socket
import time
import threading


def func():
    sk = socket.socket()
    sk.connect(('192.168.0.102', 8080))
    sk.send(b'hello')
    time.sleep(0.1)
    print(sk.recv(1024))
    sk.close()


for i in range(10):
    threading.Thread(target=func).start()

代码解释图

IO多路复用
IO多路复用的机制:
(1)select机制 windowns linux
(2)poll机制 Linux 它可以监听的对象比select机制可监听的多
1和2都是操作系统轮训每一个被监听项,看是否有读操做,随着监听项的增多,导致效率降低
(3)epoll机制 Linux 每一项都绑定了一个回调函数,有读的操作,回调函数第一时间去执行。
基于IO多路复用的模型图

server端

import socket
import select
sk = socket.socket()
sk.bind(('192.168.0.102', 8080))
sk.setblocking(False)
sk.listen(5)

read_lst = [sk]
while True:
    r_lst, w_lst, x_lst, = select.select(read_lst, [], [])
    # print(r_lst)
    # print(sk)
    for i in r_lst:
        if i is sk:
            conn, addr = i.accept()
            read_lst.append(conn)
        else:
            ret = i.recv(1024)
            if ret == b'':
                i.close()
                read_lst.remove(i)
                continue
            print(ret)
            i.send(b'goodbye!')

client端

import socket
import time
import threading


def func():
    sk = socket.socket()
    sk.connect(('192.168.0.102', 8080))
    sk.send(b'hello')
    time.sleep(3)
    print(sk.recv(1024))
    sk.close()


for i in range(10):
    threading.Thread(target=func).start()

异步IO
python不能写异步程序
异步IO模型图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值